Is it safe to use fmax for integer types in C?

226 Views Asked by At

According to the standard, fmax (the function, not macro) takes two doubles and returns a double. Is it safe to pass integers to the function?

One consideration I’m aware of is the size of the types. long long fits in double, but I don’t know if it’s always the case.

I don’t know any other considerations involved in type conversions.

fmax works fine but I want to make sure if my understanding of it is valid.

2

There are 2 best solutions below

1
possum On

You use the word "safe" at one point and then ask if it's "valid" in another. These are completely different concepts. What is valid may not be safe.

It is NOT "safe" to pass arbitrary long long and expect a good mathematical result from fmax.

Consider the following

#include <stdio.h>
#include <math.h>

int main() {
    long long a = 9223372036854775807;
    long long b = 9223372036854775806;

    double result = fmax((double)a, (double)b);

    printf("Result of fmax: %f\n", result);
    return 0;
}

which gives me

Result of fmax: 9223372036854775808.000000
2
KamilCuk On

Is it safe to use fmax for integer types in C?

It is safe. The C programming language has implicit conversions, they happen automatically. The argument will be converted to double, and then the function will be called.

The only dangerous territory is specifically the conversion, not the function itself. The function receives a double, always. The rules, from https://en.cppreference.com/w/c/language/conversion, are :

A value of any integer type can be implicitly converted to any real floating type.

  • if the value can be represented exactly by the target type, it is unchanged
  • if the value can be represented, but cannot be represented exactly, the result is an implementation-defined choice of either the nearest higher or nearest lower value, although if IEEE arithmetic is supported, rounding is to nearest. It is unspecified whether FE_INEXACT is raised in this case.
  • if the value cannot be represented, the behavior is undefined, although if IEEE arithmetic is supported, FE_INVALID is raised and the result value is unspecified.

The last case, "if the value cannot be represented", is the "unsafe" part. When the integer is big (or small) "enough" that it is greater than the biggest (or smallest) double, then it is unsafe. This rule is for any integer to double conversions in any contexts.

long long fits in double, but I don’t know if it’s always the case.

It is hard to traverse all implementations, including future ones, to find a case where the rule is broken. The C standard says that DBL_MAX has to be at least 1E+37 and LLONG_MAX has to be at least +9223372036854775807. It is perfectly allowed for an implementation to exists with LLONG_MAX greater than 1E+37 and DBL_MAX equal to 1E+37. Or any other combination, where it would be unsafe.

However, nowadays, the abundant implementations have long long in a twos-complement integer with width of 64-bits and double in the IEEE754 double-precision floating-point format float64. In such implementations, with LLONG_MAX being ~9.2e+18 and DBL_MAX holding whooping ~1.7E+308, you are safe.

So, unless you are programming for a really odd machine, where you will know that you are programming for such, you are safe. You might be interested in https://begriffs.com/posts/2018-11-15-c-portability.html . Joke: on a DeathStation 9000 converting too big integer to a double will end the life as we know it.