对于在ESP32上运行的C程序中的计算,我必须用以下方式乘除以下整数:

它为浮点变量生成3150 × 10000 ÷ 155 ÷ 138 × 100 ÷ 220 × 100.000000,为32位无符号整数生成3150 × 10000 ÷ 155 ÷ 138 × 100 ÷ 220 × 100.

我try 使用以下代码测试https://www.onlinegdb.com/的计算结果:

int main () {
    float calc = 150 * 10000 / 155 / 138 * 100 / 220 * 100 ;
    printf ( "calc = %f\n", calc ) ;    // 3100.000000

    uint32_t calc0 = 150 * 10000 / 155 / 138 * 100 / 220 * 100 ;
    printf ( "calc0 = %u\n", calc0 ) ;  // 3100
}

这再次为浮点数生成3100.000000,为32位无符号整数生成3100.

如果我在手提电脑或笔记本电脑上的计算器中输入相同的数字,两种情况下的结果都是3187,555782226.

所以,我在ESP32上的准确度损失了大约(3187−3100)÷3187×100 ~= 2,73 %(如果我没有搞砸公式的话

这种差异是从哪里来的,是如何产生的,在32位微控制器上有可能像在PC上一样得到准确的结果吗?

推荐答案

你在计算器或移动设备上的精确度没有任何损失.The precise result is 3187.5557822261889583067701...,而你的移动设备非常准确地接近了这一点.

问题是,在表达式150 * 10000 / 155 / 138 * 100 / 220 * 100中,所有乘法和除法都是在整数之间.即使您使用此表达式来初始化float,也为时已晚;精度已经丢失.

要获得更精确的结果,请通过添加.f后缀将第一个操作对象设置为float,然后所有操作都将在浮点数之间进行:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>

int main(void) {
    float calc = 150.f * 10000 / 155 / 138 * 100 / 220 * 100;
    printf( "calc = %f\n", calc );

    uint32_t calc0 = 150.f * 10000 / 155 / 138 * 100 / 220 * 100;
    // note: PRIu32 expands to the correct format specifier for uint32_t
    printf( "calc0 = %" PRIu32 "\n", calc0 );

    // tip: we can use unsigned long long (ull suffix) and shift all of the
    //      multiplications to the start to minimize precision loss
    //      (unsigned long long is needed to prevent overflow)
    uint32_t calc1 = 150ull * 10000 * 100 * 100 / 155 / 138 / 220; // 1)
    printf( "calc1 = %" PRIu32 "\n", calc1 );
}

打印:

calc = 3187.555420
calc0 = 3187
calc1 = 3187

See 100


1)我们也可以写/ (155ull * 138 * 120),而不是/ 155 / 138 / 120.The result is guaranteed to be the same.

C++相关问答推荐

理解C中的指针定义

为什么下面的C代码会进入无限循环?

如何解决C中的严格别名?

标准的C17标准是用括号将参数包装在函数声明中吗

括号中的堆栈实现错误问题

具有交换链获取和命令缓冲区提交的同步-危险-读后写错误

如何有效地编写代码来判断两个元素数量相同的数组即使在不同的位置也具有相同的元素?

S的这种管道实施有什么问题吗?

如何按顺序将所有CSV文件数据读入 struct 数组?

强制转换变量以在 struct 中蚕食

在C中包装两个数组?

收到不兼容的指针类型警告,因为函数的返回不是空*,而是 struct 指针

搜索使用int代替time_t的用法

用C++初始化局部数组变量

UpDown控制与预期相反

关于不同C编译器中的__attribute__支持

既然我们在 if 中将 int 的值更改为 10,为什么在第二个 fork 后,子进程及其创建的子进程都会打印 33 ?

为什么写入关闭管道会返回成功

clion.我无法理解 Clion 中发生的 scanf 错误

Codewars Kata 掷骰子的不稳定行为