C 中的混合类型算术和溢出

问题描述 投票:0回答:1

我有一个函数,它使用因子转换计数器值并将结果返回为无符号长整型。我需要处理可能的溢出。

IntVars.Counter_ui32:

  • unsigned long,范围为 2^32,最大值为 2^32-1 -> 4294967295

IntVars.Factor_fl32(和结果局部变量)

  • 浮点 32 位最大值 -> ~3,4E+38
  • 在另一个函数中计算,确保该因子永远不会为负

一般来说,只要值有效并且返回 unsigned long 的其余部分,我不介意结果是否溢出 unsigned long 类型。因此,我假设我需要实现 math.h 的 fmod() 函数的替代方案,因为我无法使用库函数。

unsigned long Function(IntVars_ts IntVars)
{
   float Result = (float) IntVars.Counter_ui32 * IntVars.Factor_fl32;

   //Result = Result % (2^32-1); /* modulo works with integers only */

   return (unsigned long) Result;
}

在这种情况下怎么办?我打算在 while 循环中减去 unsigned long max 值,但是正如您从输出中看到的那样,我迷失了两种不同类型的转换和可能的溢出。在这种情况下 float 没有足够的有效数字来表示 unsigned long 值吗?

注意:下面输出的最大数据类型值与我的目标平台中的不同(有效数字在上面)

从您的角度来看,还有什么需要担心的吗?如果我们只讨论丢失小数部分,则转换回 unsigned long 时的舍入错误在我的应用程序中不应成为问题。

/**************************

                            Online C Compiler.
                Code, Compile, Run and Debug C program online.
Write your code in this editor and press "Run" button to compile and execute it.

***************************/

#include <stdio.h>
#include <float.h>

//#define UI32MAX          ((unsigned long)4294967295UL)
#define UI32MAX          ((unsigned long)18446744073709551615ul)

typedef struct 
{
    unsigned long counter;
    float factor;
    
}IntVars_ts;

unsigned long Function(IntVars_ts IntVars);

int main()
{
    unsigned long start = UI32MAX - 3ul;
    printf("Maximum Unsigned Long %lu\n",(unsigned long)~0);
    printf("Maximum float %f\n\n",FLT_MAX);
    
    IntVars_ts IntVars ={start,1.0f}; /* Constant factor, I only increment the counter\
                                      \in this example */
    
    while(1)
    {
            printf("The unsigned long counter value is %lu\n",IntVars.counter);
            printf("The float counter value is %f\n",(float)IntVars.counter);
            printf("The result is %lu\n\n",Function(IntVars));
            IntVars.counter++;
    }
    


    return 0;
}

unsigned long Function(IntVars_ts IntVars)
{
    float Result = (float)IntVars.counter*IntVars.factor;
    
    while(Result >= (float)UI32MAX)
    {
        printf("Result is %f\n",Result);
        printf("Max is %f\n\n",(float)UI32MAX);
        Result -= (float)UI32MAX;
    }
    
    return (unsigned long)Result;
}
Output of the previous code is following:

/tmp/z2jTCSG5B0.o
Maximum Unsigned Long 18446744073709551615
Maximum float 340282346638528859811704183484516925440.000000

The unsigned long counter value is 18446744073709551612
The float counter value is 18446744073709551616.000000
Result is 18446744073709551616.000000
Max is 18446744073709551616.000000

The result is 0

The unsigned long counter value is 18446744073709551613
The float counter value is 18446744073709551616.000000
Result is 18446744073709551616.000000
Max is 18446744073709551616.000000

The result is 0

The unsigned long counter value is 18446744073709551614
The float counter value is 18446744073709551616.000000
Result is 18446744073709551616.000000
Max is 18446744073709551616.000000

The result is 0

The unsigned long counter value is 18446744073709551615
The float counter value is 18446744073709551616.000000
Result is 18446744073709551616.000000
Max is 18446744073709551616.000000

The result is 0

The unsigned long counter value is 0
The float counter value is 0.000000
The result is 0

The unsigned long counter value is 1
The float counter value is 1.000000
The result is 1

The unsigned long counter value is 2
The float counter value is 2.000000
The result is 2

The unsigned long counter value is 3
The float counter value is 3.000000
The result is 3
c types casting overflow
1个回答
0
投票

一般来说,只要值有效并且返回 unsigned long 的其余部分,我不介意结果是否溢出 unsigned long 类型。因此,我假设我需要实现 math.h 的 fmod() 函数的替代方案,因为我无法使用库函数。

鉴于您的

long
是 32 位宽,并且
unsigned long long
必须至少为 64,计算中间
unsigned long long
(足够注意精度)是避免比例因子较大时出现溢出问题的相当好的方法比 1. 只需将该结果分配给类型
unsigned long
即可获得所需的模数缩减。但由于这一切都依赖于所涉及类型的大小,因此最好在这里使用标准显式精度整数类型。充分考虑精度可能意味着使用
double
:

类型执行缩放计算
#include <stdint.h>

uint32_t Function(IntVars_ts IntVars) {
    double Result = (double) IntVars.counter * IntVars.factor;

    // only needed if IntVars.factor exceeds ULONG_MAX:
    double excess = (uint64_t) (Result / UINT64_MAX) * UINT64_MAX;
    Result -= excess;

    uint64_t integer_result = Result;
   
    return integer_result;
}

但是请注意,当比例因子小于 1 时,您会遇到截断问题,而不是您所询问的溢出问题。如果对函数的连续调用返回不同的结果很重要,则上述方法不适合这种情况。

© www.soinside.com 2019 - 2024. All rights reserved.