Reputation: 75
I assign 0.4543543234343654632452452525254e-323
to a double
variable a
and print it, although it is less than DBL_MIN
, it can still be assigned and printed.
DBL_MAX: 1.79769e+308
FLT_MAX: 3.40282e+38
DBL_MIN: 2.22507e-308
FLT_MIN: 1.17549e-38
a: 4.94066e-324
Why this happens?
Upvotes: 5
Views: 445
Reputation: 11022
The IEEE 754-1985 behavior is easier to understand by looking at the bits directly.
The following program shows the sign, mantissa and exponent for each number and the bit pattern for each.
#include <iostream>
#include <limits>
#include <iomanip>
#include <bitset>
#include <unordered_map>
#include <string>
#include <tuple>
#include <sstream>
#include <cmath>
using Values = std::tuple<double,std::string>;
using MyMap = std::unordered_map<std::string,Values>;
std::string convert_to_string(double val)
{
auto ptr{reinterpret_cast<const unsigned long long*>(&val)};
auto ival{*ptr};
unsigned long long mask{1ULL << 63};
std::string bitstring;
for (size_t i{0}; i<64; ++i) {
auto bitval{(ival&mask)>0};
mask >>= 1;
bitval? bitstring.push_back('1') : bitstring.push_back('0');
}
return bitstring;
}
std::ostream& operator<<(std::ostream& os,std::pair<std::string,Values> mypair)
{
auto name{mypair.first};
auto values{mypair.second};
auto dvalue{std::get<0>(values)};
auto bitsetvalue{std::get<1>(values)};
char sign_symbol{bitsetvalue.substr(0,1)=="0"?'+':'-'};
std::bitset<1> sign{bitsetvalue.substr(0,1)};
std::bitset<11> biased_exponent{bitsetvalue.substr(1,11)};
std::bitset<52> mantissa{bitsetvalue.substr(12,52)};
auto mantissa_value{mantissa.to_ullong()};
double mantissa_value_double{static_cast<double>(mantissa_value)};
auto biased_exponent_value{static_cast<signed long long>(biased_exponent.to_ulong())};
bool denormal{biased_exponent_value==0};
std::string denormal_text{denormal?"denormal":""};
signed long long exponent_value{denormal?-1022:biased_exponent_value-1023};
std::string mantissa_with_leading_digit{std::string((denormal?"0.":"1.")) + mantissa.to_string()};
double mantissa_with_leading_digit_value{double{denormal?0.0F:1.0F}+(mantissa_value_double * std::pow(2.0F,-52.0F))};
std::bitset<11> unbiased_exponent{static_cast<unsigned long long>(std::abs(exponent_value))};
char exponent_sign_symbol{exponent_value<0?'-':' '};
std::cout << std::setw(60) << name << std::setw(20) << dvalue << '\n';
std::cout << std::setw(60) << "Binary (biased exponent, hidden leading binary digit)" << std::setw(30) << sign << std::setw(15) << biased_exponent << std::setw(10) << denormal_text << std::setw(60) << mantissa << '\n';
std::cout << std::setw(60) << "Binary (unbiased exponent, visible leading binary digit)" << std::setw(30) << sign << std::setw(4) << exponent_sign_symbol << unbiased_exponent << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit << '\n';
std::cout << std::setw(60) << "Decimal (biased exponent) " << std::setw(30) << sign_symbol << std::setw(15) << biased_exponent_value << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit_value << '\n';
std::cout << std::setw(60) << "Decimal (unbiased exponent) " << std::setw(30) << sign_symbol << std::setw(15) << exponent_value << std::setw(10) << denormal_text << std::setw(60) << mantissa_with_leading_digit_value << '\n';
std::cout << std::setw(50) << mantissa_with_leading_digit_value << " * 2**" << std::setw(5) << exponent_value << " = " << std::setw(12) << mantissa_with_leading_digit_value*std::pow(2.0F,exponent_value) << '\n';
std::cout << std::setw(180) << std::setfill('-') << '\n' << std::setfill(' ') << "\n\n\n";
return os;
}
int main()
{
double max_double = std::numeric_limits<double>::max();
double lowest_double = std::numeric_limits<double>::min();
double stored_value{0.4543543234343654632452452525254e-323};
MyMap values{
{"Lowest",std::make_tuple(lowest_double, convert_to_string(lowest_double))},
{"Highest",std::make_tuple(max_double, convert_to_string(max_double))},
{"0.4543543234343654632452452525254e-323",std::make_tuple(stored_value, convert_to_string(stored_value))}
};
std::cout << std::setw(60) << "Variable name" << std::setw(20) << "Decimal value" << std::setw(10) << "Sign" << std::setw(15) << "Exponent" << std::setw(10) << "Exp. Rule" << std::setw(60) << "Mantissa" << std::setw(30) << '\n';
std::cout << std::setw(180) << std::setfill('-') << '\n' << std::setfill(' ');
for (auto& i : values)
std::cout << i;
return 0;
}
Output:
Variable name Decimal value Sign Exponent Exp. Rule Mantissa
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Lowest 2.22507e-308
Binary (biased exponent, hidden leading binary digit) 0 00000000001 0000000000000000000000000000000000000000000000000000
Binary (unbiased exponent, visible leading binary digit) 0 -01111111110 1.0000000000000000000000000000000000000000000000000000
Decimal (biased exponent) + 1 1
Decimal (unbiased exponent) + -1022 1
1 * 2**-1022 = 2.22507e-308
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Highest 1.79769e+308
Binary (biased exponent, hidden leading binary digit) 0 11111111110 1111111111111111111111111111111111111111111111111111
Binary (unbiased exponent, visible leading binary digit) 0 01111111111 1.1111111111111111111111111111111111111111111111111111
Decimal (biased exponent) + 2046 2
Decimal (unbiased exponent) + 1023 2
2 * 2** 1023 = 1.79769e+308
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
0.4543543234343654632452452525254e-323 4.94066e-324
Binary (biased exponent, hidden leading binary digit) 0 00000000000 denormal 0000000000000000000000000000000000000000000000000001
Binary (unbiased exponent, visible leading binary digit) 0 -01111111110 denormal 0.0000000000000000000000000000000000000000000000000001
Decimal (biased exponent) + 0 denormal 2.22045e-16
Decimal (unbiased exponent) + -1022 denormal 2.22045e-16
2.22045e-16 * 2**-1022 = 4.94066e-324
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Upvotes: -1
Reputation: 133619
Actually DBL_MIN
is not the smallest value but the smallest normalized value that is representable.
The difference is the leading digit with is 1 for normalized values, while it's 0 for denormal numbers. Mind that denormal numbers could suffer from sever performance issues on hardware with floating processing unit which is not able to manage them in hardware.
But your value, 0.454354e-323
, which corresponds to 4.545354e-324
is smaller than the smallest denormal number representble with a double
, indeed it gets rounded to 4.94066e-324
which is the real smallest number which can be stored in a double
.
Upvotes: 12