与atof的奇怪行为

时间:2016-10-10 14:09:13

标签: floating-point c-strings atof

我有一个Arduino从我的智能手机(通过蓝牙)接收包含unix时间戳的消息。现在,我正在尝试将DS1307与该时间戳同步。

然而,它没有用,因此,我开始搜索并在将包含时间戳的C风格数组转换为浮点数时发现了一些奇怪的行为。

// Copy the time into "timeBuff"
int timeLength = siz - _CAT_LENGTH_;
char timeBuff[timeLength+1];
memcpy(timeBuff, &msg[_CAT_LENGTH_], timeLength);
timeBuff[timeLength] = '\0';

// For debugging
Serial.print(F("timeBuff:   "));
Serial.println(timeBuff);

// Convert timeBuff string into a variable of type long
float deviceTime = atof(timeBuff);

// Now, deviceTime != what we printed above
Serial.print(F("Epoch time: "));
Serial.println(deviceTime);

前5行将消息的右侧部分复制到字符数组中,并添加终止零。

之后,我们打印timeBuff的内容并将其转换为存储在float中的deviceTime。最后,我们打印deviceTime

这是我第一次测试的结果

timeBuff:   1476113620
Epoch time: 1476113664.00

这是第二次测试

timeBuff:   1476115510
Epoch time: 1476115456.00

为什么atof的结果与我们传递的字符串不同?

2 个答案:

答案 0 :(得分:5)

在大多数平台上float以IEEE-754单精度格式表示,它是32位浮点格式,具有24位(23 + 1)有效位。它根本没有足够的精度来表示你的数字。您的数字需要大约32位才能表示。存储在float中时,大于24位的整数值通常会失去精度。

精度的损失将表现为丢失尾随位,并在最后剩余的位中进行舍入

1476113620 -> ‭01010111111110111011010011010100‬
1476113664 -> ‭01010111111110111011010100000000‬

答案 1 :(得分:2)

这是一个非常简单的程序,可以更清楚地说明同样的问题,消除涉及时间戳或atof()的任何复杂问题,结果证明这些问题无关紧要:

#include <stdio.h>

int main()
{
    float  f = 1234567890.;
    double d = 1234567890.;
    printf("f = %f\n", f);
    printf("d = %f\n", d);
    return 0;
}

我鼓励你编译这个小程序并运行它。当我这样做时,我明白了:

f = 1234567936.000000
d = 1234567890.000000

起初这似乎是不可能的。看起来它必须是编译器中的错误,或printf中的错误。如果我将简单数字“1234567890”分配给变量f,那怎么不能以这种方式打印出来?答案是,因为类型float没有足够的精度

每个人都明白,有限的精确度意味着你可能无法完全代表像1.234567890这样的数字,它可能会被破坏成1.234567936或其他东西。这里要认识到的关键是任何浮点类型(float double或其他任何东西)具有有限数量的重要性,周期数。重要的不是小数点右边的精度数字。对于大数字,您甚至不能精确地表示小数点左边的所有数字。 (事实上​​,这一点 - 你有一个有限的精度,适用于小数点的任何一侧 - 是“浮点”这个词实际上意味着。)