0

I wrote some program to test my suspicions. It contains super-ultra-reliable function, on my mind :), called less to compare integral numbers. For some combinations of types it produces results that differ from the results, which gives C++. When this happens it gives false, that you can see on the screen.

#include <iostream> 
#include <iomanip> 
#include <type_traits> 
#include <limits> 
#include <typeinfo> 

#include <cstdlib> 

#pragma GCC diagnostic ignored "-Wsign-compare" 
#pragma GCC diagnostic ignored "-Wtype-limits" 
template< typename T, typename U > 
inline 
bool less(T const & lhs, U const & rhs) 
{ 
    if (std::is_signed< T >::value && std::is_unsigned< U >::value) { 
        if (lhs < 0) { 
            return true; 
        } else if (rhs > std::numeric_limits< T >::max()) { 
            return true; 
        } else { 
            return static_cast< T >(lhs) < rhs; 
        } 
    } else if (std::is_unsigned< T >::value && std::is_signed< U >::value) { 
        if (rhs < 0) { 
            return false; 
        } else if (lhs > std::numeric_limits< T >::max()) { 
            return false; 
        } else { 
            return lhs < static_cast< T >(rhs); 
        } 
    } else { 
        return lhs < rhs; 
    } 
} 
#pragma GCC diagnostic warning "-Wtype-limits" 
#pragma GCC diagnostic warning "-Wsign-compare" 

#pragma GCC diagnostic ignored "-Wsign-compare" 
#pragma GCC diagnostic ignored "-Wtype-limits" 
template< typename U, typename S > 
void test() 
{ 
    std::cout << typeid(U).name() << " vs " << typeid(S).name() << std::endl; 
    static_assert(std::is_unsigned< U >::value && std::is_signed< S >::value, "signedness violated"); 
    static_assert(sizeof(U) != sizeof(S), "size should not be the same"); 
    U const x(std::numeric_limits< U >::max() - 2); 
    S const y(-1); 
    S const z(std::numeric_limits< S >::min()); 
    std::cout << std::boolalpha << (less(x, y) == (x < y)) << std::endl 
              << std::boolalpha << (less(y, x) == (y < x)) << std::endl 
              << std::boolalpha << (less(y, z) == (y < z)) << std::endl 
              << std::boolalpha << (less(z, y) == (z < y)) << std::endl 
              << std::boolalpha << (less(x, z) == (x < z)) << std::endl 
              << std::boolalpha << (less(z, x) == (z < x)) << std::endl 
              << std::endl; 
} 
#pragma GCC diagnostic warning "-Wtype-limits" 
#pragma GCC diagnostic warning "-Wsign-compare" 

int main() 
{ 
    using namespace std; 

    test< uint8_t,  int16_t >(); 
    test< uint8_t,  int32_t >(); 
    test< uint8_t,  int64_t >(); 
    test< uint16_t, int8_t  >(); 
    test< uint16_t, int32_t >(); 
    test< uint16_t, int64_t >(); 
    test< uint32_t, int8_t  >(); 
    test< uint32_t, int16_t >(); 
    test< uint32_t, int64_t >(); 
    test< uint64_t, int8_t  >(); 
    test< uint64_t, int16_t >(); 
    test< uint64_t, int32_t >(); 

    return EXIT_SUCCESS; 
}

I compile (bash s.sh 2>&1 | tee s.log) the program using the following script:

#!/usr/bin/env sh 

set -o errexit 
set -o verbose 

g++ -std=gnu++11 -m64 s.cpp -o s64 
g++ -std=gnu++11 -m32 s.cpp -o s32 

MINGWDIR=/c/mingw64
PATH=/usr/bin:${MINGWDIR}/bin:/c/Windows/system32:${MINGWDIR}/x86_64-w64-mingw32/lib32 ./s64 2>&1 | tee s64.log | grep -c false
PATH=/usr/bin:${MINGWDIR}/bin:/c/Windows/system32:${MINGWDIR}/x86_64-w64-mingw32/lib32 ./s32 2>&1 | tee s32.log | grep -c false

diff s32.log s64.log 

As a result (s.log) script gives the following:

g++ -std=gnu++11 -m64 s.cpp -o s64 
g++ -std=gnu++11 -m32 s.cpp -o s32 

MINGWDIR=/c/mingw64
PATH=/usr/bin:${MINGWDIR}/bin:/c/Windows/system32:${MINGWDIR}/x86_64-w64-mingw32/lib32 ./s64 2>&1 | tee s64.log | grep -c false
10
PATH=/usr/bin:${MINGWDIR}/bin:/c/Windows/system32:${MINGWDIR}/x86_64-w64-mingw32/lib32 ./s32 2>&1 | tee s32.log | grep -c false
10

diff s32.log s64.log  

As you can see the results are the same (for x32 and x64 platforms). And some of the tests have failed. Why is this happening? My program is wrong or my knowledge about C++ is little?

Tomilov Anatoliy
  • 15,657
  • 10
  • 64
  • 169

1 Answers1

2

You are testing whether the fundamental operator < functions the same as your less function. It does not.

Your function accounts for signed/unsigned mismatch and gives the mathematically correct answer.

The fundamental C++ operator will convert the signed value to an unsigned value when there's a signed/unsigned mismatch.

Drew Dormann
  • 59,987
  • 13
  • 123
  • 180