Why doesn't C have unsigned floats?

后端 未结 12 1608
名媛妹妹
名媛妹妹 2020-11-28 01:47

I know, the question seems to be strange. Programmers sometimes think too much. Please read on...

In C I use signed and unsigned integers a

相关标签:
12条回答
  • 2020-11-28 02:20

    Unsigned integer types in C are defined in such a way as to obey the rules of an abstract algebraic ring. For example, for any value X and Y, adding X-Y to Y will yield X. Unsigned integer types are guaranteed to obey these rules in all cases which do not involve conversion to or from any other numeric type [or unsigned types of different sizes], and that guarantee is one of the most important feature of such types. In some cases, it's worthwhile to give up the ability to represent negative numbers in exchange for the extra guarantees only unsigned types can provide. Floating-point types, whether signed or not, cannot abide by all the rules of an algebraic ring [e.g. they cannot guarantee that X+Y-Y will equal X], and indeed IEEE doesn't even allow them to abide by the rules of an equivalence class [by requiring that certain values compare unequal to themselves]. I don't think an "unsigned" floating-point type could abide by any axioms which an ordinary floating-point type could not, so I'm not sure what advantages it would offer.

    0 讨论(0)
  • 2020-11-28 02:24

    A square-root will definately never return a negative number. There are other places as well where a negative float value has no meaning. Perfect candidate for an unsigned float.

    C99 supports complex numbers, and a type generic form of sqrt, so sqrt( 1.0 * I) will be negative.


    The commentors highlighted a slight gloss above, in that I was referring to the type-generic sqrt macro rather than the function, and it will return a scalar floating point value by truncation of the complex to its real component:

    #include <complex.h>
    #include <tgmath.h>
    
    int main () 
    {
        complex double a = 1.0 + 1.0 * I;
    
        double f = sqrt(a);
    
        return 0;
    }
    

    It also contains a brain-fart, as the real part of the sqrt of any complex number is positive or zero, and sqrt(1.0*I) is sqrt(0.5) + sqrt(0.5)*I not -1.0.

    0 讨论(0)
  • 2020-11-28 02:26

    There is a significant difference between signed and unsigned integers in C/C++:

    value >> shift
    

    signed values leave the top bit unchanged (sign extend), unsigned values clear the top bit.

    The reason there is no unsigned float is that you quickly run into all sorts of problems if there are no negative values. Consider this:

    float a = 2.0f, b = 10.0f, c;
    c = a - b;
    

    What value does c have? -8. But what would that mean in a system without negative numbers. FLOAT_MAX - 8 perhaps? Actually, that doesn't work as FLOAT_MAX - 8 is FLOAT_MAX due to precision effects so things are even more screwy. What if it was part of a more complex expression:

    float a = 2.0f, b = 10.0f, c = 20.0f, d = 3.14159f, e;
    e = (a - b) / d + c;
    

    This isn't a problem for integers due to the nature of the 2's complement system.

    Also consider standard mathematical functions: sin, cos and tan would only work for half their input values, you couldn't find the log of values < 1, you couldn't solve quadratic equations: x = (-b +/- root (b.b - 4.a.c)) / 2.a, and so on. In fact, it probably wouldn't work for any complex function as these tend to be implemented as polynomial approximations which would use negative values somewhere.

    So, unsigned floats are pretty useless.

    But that doesn't mean to say that a class that range checks float values isn't useful, you may want to clamp values to a given range, for example RGB calculations.

    0 讨论(0)
  • 2020-11-28 02:27

    I think Treb is on the right track. It's more important for integers that you have an unsigned corresponding type. Those are the ones that are used in bit-shifting and used in bit-maps. A sign bit just gets into the way. For example, right-shifting a negative value, the resulting value is implementation defined in C++. Doing that with an unsigned integer or overflowing such one has perfectly defined semantics because there is no such bit in the way.

    So for integers at least, the need for a separate unsigned type is stronger than just giving warnings. All the above points do not need to be considered for floats. So, there is, i think, no real need for hardware support for them, and C will already don't support them at that point.

    0 讨论(0)
  • 2020-11-28 02:27

    I guess it depends on that the IEEE floating-point specifications only are signed and that most programming languages use them.

    Wikipedia article on IEEE-754 floating-point numbers

    Edit: Also, as noted by others, most hardware does not support non-negative floats, so the normal kind of floats are more efficient to do since there is hardware support.

    0 讨论(0)
  • 2020-11-28 02:29

    Good Question.

    If, as you say, it is only for compile-time warnings and no change in their behavior otherwise then the underlying hardware is not affected and as such it would only be a C++/Compiler change.

    I have wonedered the same previously, but the thing is: It would not help much. At best the compiler can find static assignments.

    unsigned float uf { 0 };
    uf = -1f;
    

    Or minimalistically longer

    unsigned float uf { 0 };
    float f { 2 };
    uf -= f;
    

    But that's about it. With unsigned integer types you also get a defined wraparound, namely it behaves like modular arithmetic.

    unsigned char uc { 0 };
    uc -= 1;
    

    after this 'uc' holds the value of 255.

    Now, what would a compiler do with the same scenario given an unsigned float-type? If the values are not know at compile time it would need to generate code that first executes the calculations and then does a sign-check. But what when the result of such a computation would be say "-5.5" - which value should be stored in a float declared unsigned? One could try modular arithmetic like for integral types, but that comes with its own problems: The largest value is unarguably infinity .... that does not work, you can not have "infinity - 1". Going for the largest distinct value it can hold also will not really work as there you run into it precission. "NaN" would be a candidate. You lose any and all information what the number originally contained - not really helpful as you now would need to check for that specifically so you might as well check if the number is positive your self.

    Lastly this would not be a problem with fixed point numbers as there modulo is well defined.

    0 讨论(0)
提交回复
热议问题