精确地提取浮点数的小数部分

Extracting decimal part of a float accurately

本文关键字:小数部 浮点数 提取      更新时间:2023-10-16

注意:我在c++的Arduino变体中编写以下代码。

从给定的浮点数(总是有四个十进制数字)中,我希望只提取小数部分("尾数")作为整数。

所以我尝试了这个方法:

void ExtractDecimalPart(float Value) {
  int IntegerPart = (int)(Value);
  int DecimalPart = 10000 * (Value - IntegerPart); //10000 b/c my float values always have exactly 4 decimal places
  Serial.println (DecimalPart);
}

但是上面的结果是:

ExtractDecimalPart (1234.5677); //prints 5677
ExtractDecimalPart (1234.5678); //prints 5677
ExtractDecimalPart (1234.5679); //prints 5678

注意后两个打印出来错误;我猜这是由于浮点精度问题。

解决上述问题的经济方法是什么?

这是个很酷的问题。

我将假设arduino使用IEEE 754当你设置一个等于1234.5677的浮点数时,最接近4字节的数字是1.2345677490234375E3,在十六进制中看起来像0x449A522B。

但是当你把1234.5678放入浮点数时,它能形成的最佳数字是1.2345677490234375E3很短。十六进制格式为0x449A522B。

所以在短浮动只是不能存储数字,需要你使用的数字的数量

避免浮点精度问题的最简单方法是将数字强制转换为字符串,然后只打印点之后的内容。

编辑

我不知道如何用arduino的c++版本做到这一点,所以请随意编辑我的答案来添加一个例子!

32位float有23位尾数,所以它只能保持大约7位的精度。你的例子使用了8个数字,所以最后一个数字就是"垃圾"。必须使用更高精度的浮点类型(标准c++中的doublelong double)。

在Arduino上,取决于变体,您可能有一个标准兼容的64位double类型或没有。如果doublefloat完全相同,则有几种解决方案:

    编写或使用double数学库。
    • 或者如果double在8位MCU上太昂贵,你可以创建一个自定义的浮点类型,如40位或48位。
    • 或者您可以像这样使用float-float算术类型。
  • 使用定点类型。由于您总是有4个十进制数字,因此这可能最适合您。
    • 您可以使用Q26.6或Q25.7二进制格式以获得更好的算术性能
    • 或者最好使用缩放后的十进制类型,最后4位数字作为小数部分,以获得更好的十进制数学输出。在某些情况下,您也可以将后4位十进制数字和整数部分分开存储,但要小心使用此实现。

你可以在本题中或在

中找到更多关于定点的信息。