ref: e5cc01a96889649c5e8151699e5634cb3b1b4d7c
dir: /include-demo/stb_divide.h/
// stb_divide.h - v0.94 - public domain - Sean Barrett, Feb 2010 // Three kinds of divide/modulus of signed integers. // // HISTORY // // v0.94 Fix integer overflow issues // v0.93 2020-02-02 Write useful exit() value from main() // v0.92 2019-02-25 Fix warning // v0.91 2010-02-27 Fix euclidean division by INT_MIN for non-truncating C // Check result with 64-bit math to catch such cases // v0.90 2010-02-24 First public release // // USAGE // // In *ONE* source file, put: // // #define STB_DIVIDE_IMPLEMENTATION // // #define C_INTEGER_DIVISION_TRUNCATES // see Note 1 // // #define C_INTEGER_DIVISION_FLOORS // see Note 2 // #include "stb_divide.h" // // Other source files should just include stb_divide.h // // Note 1: On platforms/compilers that you know signed C division // truncates, you can #define C_INTEGER_DIVISION_TRUNCATES. // // Note 2: On platforms/compilers that you know signed C division // floors (rounds to negative infinity), you can #define // C_INTEGER_DIVISION_FLOORS. // // You can #define STB_DIVIDE_TEST in which case the implementation // will generate a main() and compiling the result will create a // program that tests the implementation. Run it with no arguments // and any output indicates an error; run it with any argument and // it will also print the test results. Define STB_DIVIDE_TEST_64 // to a 64-bit integer type to avoid overflows in the result-checking // which give false negatives. // // ABOUT // // This file provides three different consistent divide/mod pairs // implemented on top of arbitrary C/C++ division, including correct // handling of overflow of intermediate calculations: // // trunc: a/b truncates to 0, a%b has same sign as a // floor: a/b truncates to -inf, a%b has same sign as b // eucl: a/b truncates to sign(b)*inf, a%b is non-negative // // Not necessarily optimal; I tried to keep it generally efficient, // but there may be better ways. // // Briefly, for those who are not familiar with the problem, we note // the reason these divides exist and are interesting: // // 'trunc' is easy to implement in hardware (strip the signs, // compute, reapply the signs), thus is commonly defined // by many languages (including C99) // // 'floor' is simple to define and better behaved than trunc; // for example it divides integers into fixed-size buckets // without an extra-wide bucket at 0, and for a fixed // divisor N there are only |N| possible moduli. // // 'eucl' guarantees fixed-sized buckets *and* a non-negative // modulus and defines division to be whatever is needed // to achieve that result. // // See "The Euclidean definition of the functions div and mod" // by Raymond Boute (1992), or "Division and Modulus for Computer // Scientists" by Daan Leijen (2001) // // We assume of the built-in C division: // (a) modulus is the remainder for the corresponding division // (b) a/b truncates if a and b are the same sign // // Property (a) requires (a/b)*b + (a%b)==a, and is required by C. // Property (b) seems to be true of all hardware but is *not* satisfied // by the euclidean division operator we define, so it's possibly not // always true. If any such platform turns up, we can add more cases. // (Possibly only stb_div_trunc currently relies on property (b).) // // LICENSE // // See end of file for license information. #ifndef INCLUDE_STB_DIVIDE_H #define INCLUDE_STB_DIVIDE_H #ifdef __cplusplus extern "C" { #endif extern int stb_div_trunc(int value_to_be_divided, int value_to_divide_by); extern int stb_div_floor(int value_to_be_divided, int value_to_divide_by); extern int stb_div_eucl (int value_to_be_divided, int value_to_divide_by); extern int stb_mod_trunc(int value_to_be_divided, int value_to_divide_by); extern int stb_mod_floor(int value_to_be_divided, int value_to_divide_by); extern int stb_mod_eucl (int value_to_be_divided, int value_to_divide_by); #ifdef __cplusplus } #endif #ifdef STB_DIVIDE_IMPLEMENTATION #if defined(__STDC_VERSION) && __STDC_VERSION__ >= 19901 #ifndef C_INTEGER_DIVISION_TRUNCATES #define C_INTEGER_DIVISION_TRUNCATES #endif #endif #ifndef INT_MIN #include <limits.h> // if you have no limits.h, #define INT_MIN yourself #endif // the following macros are designed to allow testing // other platforms by simulating them #ifndef STB_DIVIDE_TEST_FLOOR #define stb__div(a,b) ((a)/(b)) #define stb__mod(a,b) ((a)%(b)) #else // implement floor-style divide on trunc platform #ifndef C_INTEGER_DIVISION_TRUNCATES #error "floor test requires truncating division" #endif #undef C_INTEGER_DIVISION_TRUNCATES int stb__div(int v1, int v2) { int q = v1/v2, r = v1%v2; if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) return q-1; else return q; } int stb__mod(int v1, int v2) { int r = v1%v2; if ((r > 0 && v2 < 0) || (r < 0 && v2 > 0)) return r+v2; else return r; } #endif int stb_div_trunc(int v1, int v2) { #ifdef C_INTEGER_DIVISION_TRUNCATES return v1/v2; #else if (v1 >= 0 && v2 <= 0) return -stb__div(-v1,v2); // both negative to avoid overflow if (v1 <= 0 && v2 >= 0) if (v1 != INT_MIN) return -stb__div(v1,-v2); // both negative to avoid overflow else return -stb__div(v1+v2,-v2)-1; // push v1 away from wrap point else return v1/v2; // same sign, so expect truncation #endif } int stb_div_floor(int v1, int v2) { #ifdef C_INTEGER_DIVISION_FLOORS return v1/v2; #else if (v1 >= 0 && v2 < 0) { if (v2 + 1 >= INT_MIN + v1) // check if increasing v1's magnitude overflows return -stb__div((v2+1)-v1,v2); // nope, so just compute it else return -stb__div(-v1,v2) + ((-v1)%v2 ? -1 : 0); } if (v1 < 0 && v2 >= 0) { if (v1 != INT_MIN) { if (v1 + 1 >= INT_MIN + v2) // check if increasing v1's magnitude overflows return -stb__div((v1+1)-v2,-v2); // nope, so just compute it else return -stb__div(-v1,v2) + (stb__mod(v1,-v2) ? -1 : 0); } else // it must be possible to compute -(v1+v2) without overflowing return -stb__div(-(v1+v2),v2) + (stb__mod(-(v1+v2),v2) ? -2 : -1); } else return v1/v2; // same sign, so expect truncation #endif } int stb_div_eucl(int v1, int v2) { int q,r; #ifdef C_INTEGER_DIVISION_TRUNCATES q = v1/v2; r = v1%v2; #else // handle every quadrant separately, since we can't rely on q and r flor if (v1 >= 0) if (v2 >= 0) return stb__div(v1,v2); else if (v2 != INT_MIN) q = -stb__div(v1,-v2), r = stb__mod(v1,-v2); else q = 0, r = v1; else if (v1 != INT_MIN) if (v2 >= 0) q = -stb__div(-v1,v2), r = -stb__mod(-v1,v2); else if (v2 != INT_MIN) q = stb__div(-v1,-v2), r = -stb__mod(-v1,-v2); else // if v2 is INT_MIN, then we can't use -v2, but we can't divide by v2 q = 1, r = v1-q*v2; else // if v1 is INT_MIN, we have to move away from overflow place if (v2 >= 0) q = -stb__div(-(v1+v2),v2)-1, r = -stb__mod(-(v1+v2),v2); else if (v2 != INT_MIN) q = stb__div(-(v1-v2),-v2)+1, r = -stb__mod(-(v1-v2),-v2); else // for INT_MIN / INT_MIN, we need to be extra-careful to avoid overflow q = 1, r = 0; #endif if (r >= 0) return q; else return q + (v2 > 0 ? -1 : 1); } int stb_mod_trunc(int v1, int v2) { #ifdef C_INTEGER_DIVISION_TRUNCATES return v1%v2; #else if (v1 >= 0) { // modulus result should always be positive int r = stb__mod(v1,v2); if (r >= 0) return r; else return r - (v2 < 0 ? v2 : -v2); } else { // modulus result should always be negative int r = stb__mod(v1,v2); if (r <= 0) return r; else return r + (v2 < 0 ? v2 : -v2); } #endif } int stb_mod_floor(int v1, int v2) { #ifdef C_INTEGER_DIVISION_FLOORS return v1%v2; #else if (v2 >= 0) { // result should always be positive int r = stb__mod(v1,v2); if (r >= 0) return r; else return r + v2; } else { // result should always be negative int r = stb__mod(v1,v2); if (r <= 0) return r; else return r + v2; } #endif } int stb_mod_eucl(int v1, int v2) { int r = stb__mod(v1,v2); if (r >= 0) return r; else return r - (v2 < 0 ? v2 : -v2); // negative abs() [to avoid overflow] } #ifdef STB_DIVIDE_TEST #include <stdio.h> #include <math.h> #include <limits.h> int show=0; int err=0; void stbdiv_check(int q, int r, int a, int b, char *type, int dir) { if ((dir > 0 && r < 0) || (dir < 0 && r > 0)) { fprintf(stderr, "FAILED: %s(%d,%d) remainder %d in wrong direction\n", type,a,b,r); err++; } else if (b != INT_MIN) // can't compute abs(), but if b==INT_MIN all remainders are valid if (r <= -abs(b) || r >= abs(b)) { fprintf(stderr, "FAILED: %s(%d,%d) remainder %d out of range\n", type,a,b,r); err++; } #ifdef STB_DIVIDE_TEST_64 { STB_DIVIDE_TEST_64 q64 = q, r64=r, a64=a, b64=b; if (q64*b64+r64 != a64) { fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); err++; } } #else if (q*b+r != a) { fprintf(stderr, "FAILED: %s(%d,%d) remainder %d doesn't match quotient %d\n", type,a,b,r,q); err++; } #endif } void test(int a, int b) { int q,r; if (show) printf("(%+11d,%+d) | ", a,b); q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "trunc",a); q = stb_div_floor(a,b), r = stb_mod_floor(a,b); if (show) printf("(%+11d,%+2d) ", q,r); stbdiv_check(q,r,a,b, "floor",b); q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); if (show) printf("(%+11d,%+2d)\n", q,r); stbdiv_check(q,r,a,b, "euclidean",1); } void testh(int a, int b) { int q,r; if (show) printf("(%08x,%08x) |\n", a,b); q = stb_div_trunc(a,b), r = stb_mod_trunc(a,b); stbdiv_check(q,r,a,b, "trunc",a); if (show) printf(" (%08x,%08x)", q,r); q = stb_div_floor(a,b), r = stb_mod_floor(a,b); stbdiv_check(q,r,a,b, "floor",b); if (show) printf(" (%08x,%08x)", q,r); q = stb_div_eucl (a,b), r = stb_mod_eucl (a,b); stbdiv_check(q,r,a,b, "euclidean",1); if (show) printf(" (%08x,%08x)\n ", q,r); } int main(int argc, char **argv) { if (argc > 1) show=1; test(8,3); test(8,-3); test(-8,3); test(-8,-3); test(1,2); test(1,-2); test(-1,2); test(-1,-2); test(8,4); test(8,-4); test(-8,4); test(-8,-4); test(INT_MAX,1); test(INT_MIN,1); test(INT_MIN+1,1); test(INT_MAX,-1); //test(INT_MIN,-1); // this traps in MSVC, so we leave it untested test(INT_MIN+1,-1); test(INT_MIN,-2); test(INT_MIN+1,2); test(INT_MIN+1,-2); test(INT_MAX,2); test(INT_MAX,-2); test(INT_MIN+1,2); test(INT_MIN+1,-2); test(INT_MIN,2); test(INT_MIN,-2); test(INT_MIN,7); test(INT_MIN,-7); test(INT_MIN+1,4); test(INT_MIN+1,-4); testh(-7, INT_MIN); testh(-1, INT_MIN); testh(1, INT_MIN); testh(7, INT_MIN); testh(INT_MAX-1, INT_MIN); testh(INT_MAX, INT_MIN); testh(INT_MIN, INT_MIN); testh(INT_MIN+1, INT_MIN); testh(INT_MAX-1, INT_MAX); testh(INT_MAX , INT_MAX); testh(INT_MIN , INT_MAX); testh(INT_MIN+1, INT_MAX); return err > 0 ? 1 : 0; } #endif // STB_DIVIDE_TEST #endif // STB_DIVIDE_IMPLEMENTATION #endif // INCLUDE_STB_DIVIDE_H /* ------------------------------------------------------------------------------ This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License Copyright (c) 2017 Sean Barrett Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ ALTERNATIVE B - Public Domain (www.unlicense.org) This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------ */