在 C 小数点数据类型中无法精确表示的最小值是什么?

What are the smallest values that can no longer be represented exactly in C decimal point datatypes?

提问人:HeapUnderStop 提问时间:5/22/2023 最后编辑:Weather VaneHeapUnderStop 更新时间:5/23/2023 访问量:285

问:

例如:不能再精确表示为 的最小值是多少?等。uint32_tdouble

以及如何计算这些值?

c 浮点 精度

评论

3赞 Some programmer dude 5/22/2023
整数是一个不好的例子,因为整数类型可以表示类型范围内的所有值。浮点类型是一个不同的问题。为此,请参阅浮点类型的限制参考
0赞 Weather Vane 5/22/2023
我认为 C23 标准涵盖了十进制浮点类型,但不是“小数点”类型。它表示指定范围内的精确整数值。uint32_t
4赞 Weather Vane 5/22/2023
重新编辑:的每个值都可以用 精确表示,因为它的有效位超过 32 位。uint32_tdouble
2赞 n. m. could be an AI 5/22/2023
double不是小数点数据类型。所有值都完全可以表示为(在符合 IEEE-754 的实现中)。uint32_tdouble
2赞 chux - Reinstate Monica 5/22/2023
@n.m. C 确实允许十进制,尽管现在这种情况很少见。也不需要符合 IEEE-754 就可以让我们知道 C 需要 a 来编码所有给定的。doubledoubleuint32_tDBL_DIG >= 10

答:

2赞 ikegami 5/22/2023 #1

What 和 are 因编译器而异,它根据架构进行选择。在 x86 或 x86-64 上,a 可能是 IEEE 单精度浮点数,也可能是 IEEE 双精度浮点数。floatdoublefloatdouble

类型 连续
可表示整数
的范围(非独占边界)
此范围的大小
uint16_t 0 ..2^16-1 2^16
uint32_t 0 ..2^32-1 2^32
uint64_t 0 ..2^64-1 2^64
IEEE单精度 -2^24 ..2^24 2^25+1
IEEE双精度 -2^53 ..2^53 2^54+1

IEEE 单精度浮点数具有 24 位精度。[1] 它可以准确地表示比上面提到的大得多的数字,但它不能完全代表 2^24+1 或 -2^24-1。

IEEE 双精度浮点数具有 53 位精度。[1] 它可以准确地表示比上面提到的大得多的数字,但它不能完全代表 2^53+1 或 -2^53-1。


  1. 次正态值(极小数字)的精度较低。
3赞 chux - Reinstate Monica 5/22/2023 #2

不能再精确表示为 的最小值是多少?uint32_tdouble

所有这些都可以精确地表示为 a,因为 C 指定 C 编码的结果至少是所有连续的整数值 [-10 10 到 +1010]。这包括所有 .uint32_tdoubleDBL_DIG >= 10uint32_t


如何计算这些值?

请参阅 C 规范。

整数值的最小连续范围完全可编码为浮点类型,主要由 C 指定:

FLT_DIG 6
DBL_DIG 10
LDBL_DIG 10

在 的情况下,所有 6 个十进制数字整数都可以表示 [-999,999.0 至 +999,999.0],包括 +/- 1,000,000。从零开始向下,第一个可能无法表示为 a 的负整数值是 -1,000,001。这是 时的最小范围,几乎不存在了。floatfloatFLT_RADIX == 10

当(很常见)时,有效位数中的二进制位数为:FLT_RADIX == 2p

(p-1)*log10(2) >= xxx_DIG

求解这个问题,至少是 20。使用 ,可以精确编码整数 [-2 20 到 +220] 或 [-1,048,576 到 +1,048,576]。floatpp == 20float

最低规格就这么多。


公共限制

具有符号位和 24 个二进制数字的典型浮点数的范围比 C 规范最小值更宽。它精确编码整数 [-224 至 +224]。第一个不可表示的负整数值:-224 - 1 或 -16,777,217。

1赞 Netch 5/23/2023 #3

1. 你的问题有一个主要差异:你说了“小数点”数据类型,但随后提到了“双精度”。

double,在大多数可用实现中,它不是小数点类型,而是二进制点类型(IEEE754 64 位十进制)。您可能会在互联网和文献中更详细地查看它们之间的区别。

对于以下内容,我将假设二进制而不是(实际上很少存在和使用)真正的十进制类型。double

2. 嗯,很简单,但有点棘手:))

我将假设您引用的平台本机实现了 IEEE754,并且是 32 位二进制 IEEE 编号和 64 位二进制 IEEE 编号。 在本例中,具有前导 1 的 24 位尾数(我们在这里不计算非正态),我们需要最小的值,不包括前导尾随零,不适合 24 位。(无需检查问题的指数范围。floatdoublefloat

对于以下内容,“0b”是二进制前缀,“**”是幂运算符。

16777215 = 2**24-1 是 0b11111111111111111111111111(连续 24 个)。它适合。

16777216 = 2**24 是 0b1000000000000000000000000000 (1 和 24 个连续零)。它适合。

16777217 = 2**24+1 是 0b10000000000000000000000001(1、23 个连续零和 1)。它不合适。

“浮动”可以表示:...、16777214、16777215、16777216、16777218、16777220、16777222......因此,从 16777216 = 2**24 开始,可表示值之间的步长为 2。

因此,在“float”中无法表示的最小无符号整数为 16777217。

无需为其他情况复制所有这些长字符串位 - 使用其 53 位尾数,它会很麻烦。我希望这个原则得到很好的宣布。对于您的具体示例,这意味着任何值都可以用 精确表示,但不能用 表示。doubleuint32_tdoublefloat

3. 此外,您可能只是检查了声明的准确性。 最多 10 位十进制数字(保证 9 位)。 最多需要 17 位十进制数字才能准确表示任何值,并保证 15 位十进制数字表示。范围之间存在明显差异,因此无需进行更精确的边界检查。uint32_tdoubleuint32_tdouble