File: GB_uint64_multiply.h

package info (click to toggle)
suitesparse 1%3A7.10.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 254,920 kB
  • sloc: ansic: 1,134,743; cpp: 46,133; makefile: 4,875; fortran: 2,087; java: 1,826; sh: 996; ruby: 725; python: 495; asm: 371; sed: 166; awk: 44
file content (97 lines) | stat: -rw-r--r-- 2,847 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
//------------------------------------------------------------------------------
// GB_uint64_multiply:  multiply two uint64_t and guard against overflow
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2025, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//------------------------------------------------------------------------------

// c = a*b but check for overflow

// If c=a*b is large, c = UINT64_MAX is set, and the function returns false.
// "large" means that c=a*b might overflow; see details below.

// Otherwise c = a*b < INT64_MAX is guaranteed to be returned, and the function
// returns true.  In this case, c = a*b > GB_NMAX = (2^60)+1 may be returned,
// so if the caller requires c <= GB_NMAX, that must be checked after computing
// c=a*b with this function.

#ifndef GB_UINT64_MULTIPLY_H
#define GB_UINT64_MULTIPLY_H

#define GB_TWO_TO_THE_30 (0x40000000L)

GB_STATIC_INLINE bool GB_uint64_multiply    // c = a*b, return true if ok
(
    uint64_t *restrict c,
    const uint64_t a,
    const uint64_t b
)
{

    if (a <= 1 || b <= 1)
    { 
        (*c) = a*b ;
        return (true) ;
    }

    uint64_t a1 = a >> 30 ;     // a1 = a / 2^30
    uint64_t b1 = b >> 30 ;     // b1 = b / 2^30
    if (a1 > 0 && b1 > 0)
    { 
        // c = a*b will likely overflow, since both a and b are >= 2^30 and
        // thus c >= 2^60.  This is slightly pessimistic, but the function does
        // not need to compute the exact result in this case.  If a full matrix
        // has size a-by-b in this case, it will have 2^60 entries or more,
        // which is impossible.
        (*c) = UINT64_MAX ;
        return (false) ;
    }

    // a = a1 * 2^30 + a0
    uint64_t a0 = a & 0x3FFFFFFFL ;

    // b = b1 * 2^30 + b0
    uint64_t b0 = b & 0x3FFFFFFFL ;

    // a*b = (a1*b1) * 2^60 + (a1*b0 + a0*b1) * 2^30 + a0*b0

    // since either a1 or b1 are zero, a1*b1 is zero

    // a0, b0 are < 2^30
    // a1, b1 are < 2^34

    // a1*b0 < 2^64
    // a0*b1 < 2^64
    // a0*b0 < 2^60
    // thus

    // a*b = (a1*b0 + a0*b1) * 2^30 + a0*b0
    //     < (2^64  + 2^64 ) * 2^30 + 2^60

    // so it is safe to compute t0 and t1 without risk of overflow:

    uint64_t t0 = a1*b0 ;
    uint64_t t1 = a0*b1 ;

    // a*b = (t0 + t1) * 2^30 + a0*b0

    if (t0 >= GB_TWO_TO_THE_30 || t1 >= GB_TWO_TO_THE_30)
    { 
        // t >= 2^30, so t * 2^30 might overflow.  This is also slightly
        // pessimistic, but good enough for the usage of this function.
        (*c) = UINT64_MAX ;
        return (false) ;
    }

    // t = t0 + t1 < 2^30 + 2^30 < 2^31, so

    // c = a*b = t * 2^30 + a0*b0 < 2^61 + 2^60 < 2^62, no overflow possible
    uint64_t t = t0 + t1 ;
    (*c) = (t << 30) + a0*b0 ;
    return (true) ;
}

#endif