Gre*_*ree 19 c floating-point standards
我需要编写函数double_to_int(double val, int *err),在可能的情况下,它会将double val转换为整数; 否则报告错误(NAN/INFs/OUT_OF_RANGE).
所以伪代码实现看起来像:
if isnan(val):
err = ERR_NAN
return 0
if val < MAX_INT:
err = ERR_MINUS_INF
return MIN_INT
if ...
return (int)val
Run Code Online (Sandbox Code Playgroud)
在SO上至少有两个类似的问题:在这个答案中它以足够干净的方式解决了,虽然它是C++解决方案 - 在C中我们没有signed int的可移植数字.在这个答案中,它解释了为什么我们不能只检查(val > INT_MAX || val < INT_MIN).
因此,我看到的唯一可能的清洁方式是使用浮点环境,但它被称为实现定义的功能.
所以我的问题是:有没有办法以double_to_int跨平台的方式实现功能(仅基于C标准,甚至不考虑支持IEEE-754的目标平台).
[这个答案已经用全新的方法进行了编辑。]
\n\n此方法使用 C 标准\xe2\x80\x94 中浮点格式的定义作为有符号的基数b数字乘以b的幂。知道有效数字的位数(由 提供DBL_MANT_DIG)和指数限制(由 提供DBL_MAX_EXP)使我们能够准备精确的double值作为终点。
我相信它会在所有符合 C 实现的情况下工作,但要满足最初评论中所述的适度附加要求。
\n\n/* This code demonstrates safe conversion of double to int in which the\n input double is converted to int if and only if it is in the supported\n domain for such conversions (the open interval (INT_MIN-1, INT_MAX+1)).\n If the input is not in range, an error is indicated (by way of an\n auxiliary argument) and no conversion is performed, so all behavior is\n defined.\n\n There are a few requirements not fully covered by the C standard. They\n should be uncontroversial and supported by all reasonable C implementations:\n\n Conversion of an int that is representable in double produces the\n exact value.\n\n The following operations are exact in floating-point:\n\n Dividing by the radix of the floating-point format, within its\n range.\n\n Multiplying by +1 or -1.\n\n Adding or subtracting two values whose sum or difference is\n representable.\n\n FLT_RADIX is representable in int.\n\n DBL_MIN_EXP is not greater than -DBL_MANT_DIG. (The code can be\n modified to eliminate this requirement.)\n\n Deviations from the requested routine include:\n\n This code names the routine DoubleToInt instead of double_to_int.\n\n The only error indicated is ERANGE. Code to distinguish the error more\n finely, such as providing separate values for NaNs, infinities, and\n out-of-range finite values, could easily be added.\n*/\n\n\n#include <float.h>\n#include <errno.h>\n#include <limits.h>\n#include <stdio.h>\n\n\n/* These values will be initialized to the greatest double value not greater\n than INT_MAX+1 and the least double value not less than INT_MIN-1.\n*/\nstatic double UpperBound, LowerBound;\n\n\n/* Return the double of the same sign of x that has the greatest magnitude\n less than x+s, where s is -1 or +1 according to whether x is negative or\n positive.\n*/\nstatic double BiggestDouble(int x)\n{\n /* All references to "digits" in this routine refer to digits in base\n FLT_RADIX. For example, in base 3, 77 would have four digits (2212).\n\n In this routine, "bigger" and "smaller" refer to magnitude. (3 is\n greater than -4, but -4 is bigger than 3.)\n */\n\n // Determine the sign.\n int s = 0 < x ? +1 : -1;\n\n // Count how many digits x has.\n int digits = 0;\n for (int t = x; t; ++digits)\n t /= FLT_RADIX;\n\n /* If the double type cannot represent finite numbers this big, return the\n biggest finite number it can hold, with the desired sign.\n */\n if (DBL_MAX_EXP < digits)\n return s*DBL_MAX;\n\n // Determine whether x is exactly representable in double.\n if (DBL_MANT_DIG < digits)\n {\n /* x is not representable, so we will return the next lower\n representable value by removing just as many low digits as\n necessary. Note that x+s might be representable, but we want to\n return the biggest double less than it, which is also the biggest\n double less than x.\n */\n\n /* Figure out how many digits we have to remove to leave at most\n DBL_MANT_DIG digits.\n */\n digits = digits - DBL_MANT_DIG;\n\n // Calculate FLT_RADIX to the power of digits.\n int t = 1;\n while (digits--) t *= FLT_RADIX;\n\n return x / t * t;\n }\n else\n {\n /* x is representable. To return the biggest double smaller than\n x+s, we will fill the remaining digits with FLT_RADIX-1.\n */\n\n // Figure out how many additional digits double can hold.\n digits = DBL_MANT_DIG - digits;\n\n /* Put a 1 in the lowest available digit, then subtract from 1 to set\n each digit to FLT_RADIX-1. (For example, 1 - .001 = .999.)\n */\n double t = 1;\n while (digits--) t /= FLT_RADIX;\n t = 1-t;\n\n // Return the biggest double smaller than x+s.\n return x + s*t;\n }\n}\n\n\n/* Set up supporting data for DoubleToInt. This should be called once prior\n to any call to DoubleToInt.\n*/\nstatic void InitializeDoubleToInt(void)\n{\n UpperBound = BiggestDouble(INT_MAX);\n LowerBound = BiggestDouble(INT_MIN);\n}\n\n\n/* Perform the conversion. If the conversion is possible, return the\n converted value and set *error to zero. Otherwise, return zero and set\n *error to ERANGE.\n*/\nstatic int DoubleToInt(double x, int *error)\n{\n if (LowerBound <= x && x <= UpperBound)\n {\n *error = 0;\n return x;\n }\n else\n {\n *error = ERANGE;\n return 0;\n }\n}\n\n\n#include <string.h>\n\n\nstatic void Test(double x)\n{\n int error, y;\n y = DoubleToInt(x, &error);\n printf("%.99g -> %d, %s.\\n", x, y, error ? strerror(error) : "No error");\n}\n\n\n#include <math.h>\n\n\nint main(void)\n{\n InitializeDoubleToInt();\n printf("UpperBound = %.99g\\n", UpperBound);\n printf("LowerBound = %.99g\\n", LowerBound);\n\n Test(0);\n Test(0x1p31);\n Test(nexttoward(0x1p31, 0));\n Test(-0x1p31-1);\n Test(nexttoward(-0x1p31-1, 0));\n}\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
677 次 |
| 最近记录: |