I'm writing classes for basic types, so that code is logically the same on multiple platforms and compilers (like int_least16_t
for int
). For fun! (I'm still a student.)
And I read this:
And what's worse:
Floating-point types MAY support special values: ∞, NaN or -0
Which means that float MAY be unsigned...
[edit: Yes it's difrent thing, but there is no: ",but must suport negative numbers". Yo, with out things like this in standart it may not suport normal 0...(I don't have specyfication.) see]
I know it's like with __int128, and the standard is just a standard, but still... IEEE-754 is from 1985, but some machines can be weird, and some legacy hardware doesn't have a floating unit.
As I understand, float is mandatory (not optional like int16_t), but can be in any standard, and any set of values can be possible?
Only thing we have are some macros (<cfloat>
):
FLT_MIN
,FLT_MAX
- Even ifFLT_MIN = IEEE-754::FLT_MIN
, float can be non IEEE-754. For example float with: flipped exponent with fraction...FLT_RADIX
- Base system? If so, can help to write the exact value. But still, float can be 3 bit or 200 bit (in size)...FLT_EPSILON
- (from 1 to next) We might use it (with base) to check fraction size...FLT_MANT_DIG
- Is it "mantissa" digits / fraction size?FLT_MAX_EXP
- Exponent filled with 1... in IEEE-754, but outside can be a random number?
If float is like IEEE-754 (sign, exponent, fraction), then it's easy,
but if -0 and NaN are optional then it MAY be different.
Because I can't tell them apart, I can't use bit representation
(in a safe manner). And if ∞ is optional, float
is no longer a safe type.
The only way out I see is to add macro to compiler.
I know it's a theoretical problem, but I'm interested if there is any check possible or we all write implementation dependent code, when we use the float
keyword?
Edit 2022 May 04:
I came up with this:
User eg. code:
//User eg. code:
int main()
{
float_M a = 1f;
float_M b = 0f;
std::cout << a/b; //should output infinty (IEEE-754)
}
//Code:
class float_M
{
public:
#ifdef __STDC_IEC_559__
float data;
//...
float_M operator/(float_M x){return float_M(data/x.data);}
//...
#else
/*union{
float data;
struct{//For noSign case ("absolutly catastrofic" case)
uint_least8_t sign : 1;
uint_least8_t exponent : 8;
uint_least32_t fraction : 23;
}
}*/ //no noSign case
float data;
//...
float_M operator/(float_M x){return divide(this, x);}
//funtion pointer alert!
static /*const (1*) */ float_M (*divide)(float_M a, float_M b) =
/*std::numeric_limits<float>::is_signed ?(*/
std::numeric_limits<float>::has_infinity ?(
std::numeric_limits<float>::has_quiet_NaN ?(
[]{return float_M(a.data/b.data);}
): &_divide_noNaN
): &_divide_noNaN
/*): &_divide_noSign*/
//...
#endif
}
It's ugly (have funtion pointer), but prevents unnesesery jumps at runtime. I hope c++23 will have better macros.
Also, more links:
Follow-up: Can floats not suport negative