Skip to content

Commit

Permalink
div64_u64(): improve precision on 32bit platforms
Browse files Browse the repository at this point in the history
The current implementation of div64_u64 for 32bit systems returns an
approximately correct result when the divisor exceeds 32bits.  Since doing
64bit division using 32bit hardware is a long since solved problem we just
use one of the existing proven methods.

Additionally, add a div64_s64 function to correctly handle doing signed
64bit division.

Addresses https://bugzilla.redhat.com/show_bug.cgi?id=616105

Signed-off-by: Brian Behlendorf <[email protected]>
Signed-off-by: Oleg Nesterov <[email protected]>
Cc: Ben Woodard <[email protected]>
Cc: Jeremy Fitzhardinge <[email protected]>
Cc: Mark Grondona <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
behlendorf authored and torvalds committed Oct 26, 2010
1 parent 5d051de commit 658716d
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 10 deletions.
5 changes: 5 additions & 0 deletions include/linux/kernel.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ extern int _cond_resched(void);
(__x < 0) ? -__x : __x; \
})

#define abs64(x) ({ \
s64 __x = (x); \
(__x < 0) ? -__x : __x; \
})

#ifdef CONFIG_PROVE_LOCKING
void might_fault(void);
#else
Expand Down
12 changes: 12 additions & 0 deletions include/linux/math64.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ static inline u64 div64_u64(u64 dividend, u64 divisor)
return dividend / divisor;
}

/**
* div64_s64 - signed 64bit divide with 64bit divisor
*/
static inline s64 div64_s64(s64 dividend, s64 divisor)
{
return dividend / divisor;
}

#elif BITS_PER_LONG == 32

#ifndef div_u64_rem
Expand All @@ -53,6 +61,10 @@ extern s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder);
extern u64 div64_u64(u64 dividend, u64 divisor);
#endif

#ifndef div64_s64
extern s64 div64_s64(s64 dividend, s64 divisor);
#endif

#endif /* BITS_PER_LONG */

/**
Expand Down
52 changes: 42 additions & 10 deletions lib/div64.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,26 +77,58 @@ s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
EXPORT_SYMBOL(div_s64_rem);
#endif

/* 64bit divisor, dividend and result. dynamic precision */
/**
* div64_u64 - unsigned 64bit divide with 64bit divisor
* @dividend: 64bit dividend
* @divisor: 64bit divisor
*
* This implementation is a modified version of the algorithm proposed
* by the book 'Hacker's Delight'. The original source and full proof
* can be found here and is available for use without restriction.
*
* 'http://www.hackersdelight.org/HDcode/newCode/divDouble.c'
*/
#ifndef div64_u64
u64 div64_u64(u64 dividend, u64 divisor)
{
u32 high, d;
u32 high = divisor >> 32;
u64 quot;

high = divisor >> 32;
if (high) {
unsigned int shift = fls(high);
if (high == 0) {
quot = div_u64(dividend, divisor);
} else {
int n = 1 + fls(high);
quot = div_u64(dividend >> n, divisor >> n);

d = divisor >> shift;
dividend >>= shift;
} else
d = divisor;
if (quot != 0)
quot--;
if ((dividend - quot * divisor) >= divisor)
quot++;
}

return div_u64(dividend, d);
return quot;
}
EXPORT_SYMBOL(div64_u64);
#endif

/**
* div64_s64 - signed 64bit divide with 64bit divisor
* @dividend: 64bit dividend
* @divisor: 64bit divisor
*/
#ifndef div64_s64
s64 div64_s64(s64 dividend, s64 divisor)
{
s64 quot, t;

quot = div64_u64(abs64(dividend), abs64(divisor));
t = (dividend ^ divisor) >> 63;

return (quot ^ t) - t;
}
EXPORT_SYMBOL(div64_s64);
#endif

#endif /* BITS_PER_LONG == 32 */

/*
Expand Down

0 comments on commit 658716d

Please sign in to comment.