2

我有一个 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与我们传递的字符串不同?

4

2 回答 2

5

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

精度损失将表现为尾随位丢失,最后剩余位舍入

1476113620 -> ‭01010111111110111011010011010100‬
1476113664 -> ‭01010111111110111011010100000000‬
于 2016-10-10T14:14:20.040 回答
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,它怎么能不打印出来呢?答案是,因为 typefloat没有足够的精度

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

于 2016-10-10T14:20:24.020 回答