pakade
pakade

Reputation: 75

why a double number less than DBL_MIN can be printed?

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

Answers (2)

wally
wally

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

Jack
Jack

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

Related Questions