s_modf.c 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*******************************************************************************
  2. ** File: rndint.c
  3. **
  4. ** Contains: C source code for implementations of floating-point
  5. ** functions which round to integral value or format, as
  6. ** defined in header <fp.h>. In particular, this file
  7. ** contains implementations of functions rint, nearbyint,
  8. ** rinttol, round, roundtol, trunc, modf and modfl. This file
  9. ** targets PowerPC or Power platforms.
  10. **
  11. ** Written by: A. Sazegari, Apple AltiVec Group
  12. ** Created originally by Jon Okada, Apple Numerics Group
  13. **
  14. ** Copyright: © 1992-2001 by Apple Computer, Inc., all rights reserved
  15. **
  16. ** Change History (most recent first):
  17. **
  18. ** 13 Jul 01 ram replaced --setflm calls with inline assembly
  19. ** 03 Mar 01 ali first port to os x using gcc, added the crucial __setflm
  20. ** definition.
  21. ** 1. removed double_t, put in double for now.
  22. ** 2. removed iclass from nearbyint.
  23. ** 3. removed wrong comments intrunc.
  24. ** 4.
  25. ** 13 May 97 ali made performance improvements in rint, rinttol, roundtol
  26. ** and trunc by folding some of the taligent ideas into this
  27. ** implementation. nearbyint is faster than the one in taligent,
  28. ** rint is more elegant, but slower by %30 than the taligent one.
  29. ** 09 Apr 97 ali deleted modfl and deferred to AuxiliaryDD.c
  30. ** 15 Sep 94 ali Major overhaul and performance improvements of all functions.
  31. ** 20 Jul 94 PAF New faster version
  32. ** 16 Jul 93 ali Added the modfl function.
  33. ** 18 Feb 93 ali Changed the return value of fenv functions
  34. ** feclearexcept and feraiseexcept to their new
  35. ** NCEG X3J11.1/93-001 definitions.
  36. ** 16 Dec 92 JPO Removed __itrunc implementation to a
  37. ** separate file.
  38. ** 15 Dec 92 JPO Added __itrunc implementation and modified
  39. ** rinttol to include conversion from double
  40. ** to long int format. Modified roundtol to
  41. ** call __itrunc.
  42. ** 10 Dec 92 JPO Added modf (double) implementation.
  43. ** 04 Dec 92 JPO First created.
  44. **
  45. *******************************************************************************/
  46. #include <limits.h>
  47. #include <math.h>
  48. #define SET_INVALID 0x01000000UL
  49. typedef union
  50. {
  51. struct {
  52. #if defined(__BIG_ENDIAN__)
  53. unsigned long int hi;
  54. unsigned long int lo;
  55. #else
  56. unsigned long int lo;
  57. unsigned long int hi;
  58. #endif
  59. } words;
  60. double dbl;
  61. } DblInHex;
  62. static const unsigned long int signMask = 0x80000000ul;
  63. static const double twoTo52 = 4503599627370496.0;
  64. static const double doubleToLong = 4503603922337792.0; // 2^52
  65. static const DblInHex Huge = {{ 0x7FF00000, 0x00000000 }};
  66. static const DblInHex TOWARDZERO = {{ 0x00000000, 0x00000001 }};
  67. /*******************************************************************************
  68. * *
  69. * The function nearbyint rounds its double argument to integral value *
  70. * according to the current rounding direction and returns the result in *
  71. * double format. This function does not signal inexact. *
  72. * *
  73. ********************************************************************************
  74. * *
  75. * This function calls fabs and copysign. *
  76. * *
  77. *******************************************************************************/
  78. double nearbyint ( double x )
  79. {
  80. double y;
  81. double OldEnvironment;
  82. y = twoTo52;
  83. asm ("mffs %0" : "=f" (OldEnvironment)); /* get the environement */
  84. if ( fabs ( x ) >= y ) /* huge case is exact */
  85. return x;
  86. if ( x < 0 ) y = -y; /* negative case */
  87. y = ( x + y ) - y; /* force rounding */
  88. if ( y == 0.0 ) /* zero results mirror sign of x */
  89. y = copysign ( y, x );
  90. // restore old flags
  91. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment ));
  92. return ( y );
  93. }
  94. /*******************************************************************************
  95. * *
  96. * The function rinttol converts its double argument to integral value *
  97. * according to the current rounding direction and returns the result in *
  98. * long int format. This conversion signals invalid if the argument is a *
  99. * NaN or the rounded intermediate result is out of range of the *
  100. * destination long int format, and it delivers an unspecified result in *
  101. * this case. This function signals inexact if the rounded result is *
  102. * within range of the long int format but unequal to the operand. *
  103. * *
  104. *******************************************************************************/
  105. long int rinttol ( double x )
  106. {
  107. register double y;
  108. DblInHex argument, OldEnvironment;
  109. unsigned long int xHead;
  110. register long int target;
  111. argument.dbl = x;
  112. target = ( argument.words.hi < signMask ); // flag positive sign
  113. xHead = argument.words.hi & 0x7ffffffful; // high 32 bits of x
  114. if ( target )
  115. /*******************************************************************************
  116. * Sign of x is positive. *
  117. *******************************************************************************/
  118. {
  119. if ( xHead < 0x41dffffful )
  120. { // x is safely in long range
  121. y = ( x + twoTo52 ) - twoTo52; // round at binary point
  122. argument.dbl = y + doubleToLong; // force result into argument.words.lo
  123. return ( ( long ) argument.words.lo );
  124. }
  125. asm ("mffs %0" : "=f" (OldEnvironment.dbl)); // get environment
  126. if ( xHead > 0x41dffffful )
  127. { // x is safely out of long range
  128. OldEnvironment.words.lo |= SET_INVALID;
  129. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  130. return ( LONG_MAX );
  131. }
  132. /*******************************************************************************
  133. * x > 0.0 and may or may not be out of range of long. *
  134. *******************************************************************************/
  135. y = ( x + twoTo52 ) - twoTo52; // do rounding
  136. if ( y > ( double ) LONG_MAX )
  137. { // out of range of long
  138. OldEnvironment.words.lo |= SET_INVALID;
  139. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  140. return ( LONG_MAX );
  141. }
  142. argument.dbl = y + doubleToLong; // in range
  143. return ( ( long ) argument.words.lo ); // return result & flags
  144. }
  145. /*******************************************************************************
  146. * Sign of x is negative. *
  147. *******************************************************************************/
  148. if ( xHead < 0x41e00000ul )
  149. { // x is safely in long range
  150. y = ( x - twoTo52 ) + twoTo52;
  151. argument.dbl = y + doubleToLong;
  152. return ( ( long ) argument.words.lo );
  153. }
  154. asm ("mffs %0" : "=f" (OldEnvironment.dbl)); // get environment
  155. if ( xHead > 0x41e00000ul )
  156. { // x is safely out of long range
  157. OldEnvironment.words.lo |= SET_INVALID;
  158. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  159. return ( LONG_MIN );
  160. }
  161. /*******************************************************************************
  162. * x < 0.0 and may or may not be out of range of long. *
  163. *******************************************************************************/
  164. y = ( x - twoTo52 ) + twoTo52; // do rounding
  165. if ( y < ( double ) LONG_MIN )
  166. { // out of range of long
  167. OldEnvironment.words.lo |= SET_INVALID;
  168. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  169. return ( LONG_MIN );
  170. }
  171. argument.dbl = y + doubleToLong; // in range
  172. return ( ( long ) argument.words.lo ); // return result & flags
  173. }
  174. /*******************************************************************************
  175. * *
  176. * The function round rounds its double argument to integral value *
  177. * according to the "add half to the magnitude and truncate" rounding of *
  178. * Pascal's Round function and FORTRAN's ANINT function and returns the *
  179. * result in double format. This function signals inexact if an ordered *
  180. * return value is not equal to the operand. *
  181. * *
  182. *******************************************************************************/
  183. double round ( double x )
  184. {
  185. DblInHex argument, OldEnvironment;
  186. register double y, z;
  187. register unsigned long int xHead;
  188. register long int target;
  189. argument.dbl = x;
  190. xHead = argument.words.hi & 0x7fffffffUL; // xHead <- high half of |x|
  191. target = ( argument.words.hi < signMask ); // flag positive sign
  192. if ( xHead < 0x43300000ul )
  193. /*******************************************************************************
  194. * Is |x| < 2.0^52? *
  195. *******************************************************************************/
  196. {
  197. if ( xHead < 0x3ff00000ul )
  198. /*******************************************************************************
  199. * Is |x| < 1.0? *
  200. *******************************************************************************/
  201. {
  202. asm ("mffs %0" : "=f" (OldEnvironment.dbl)); // get environment
  203. if ( xHead < 0x3fe00000ul )
  204. /*******************************************************************************
  205. * Is |x| < 0.5? *
  206. *******************************************************************************/
  207. {
  208. if ( ( xHead | argument.words.lo ) != 0ul )
  209. OldEnvironment.words.lo |= 0x02000000ul;
  210. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  211. if ( target )
  212. return ( 0.0 );
  213. else
  214. return ( -0.0 );
  215. }
  216. /*******************************************************************************
  217. * Is 0.5 ² |x| < 1.0? *
  218. *******************************************************************************/
  219. OldEnvironment.words.lo |= 0x02000000ul;
  220. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  221. if ( target )
  222. return ( 1.0 );
  223. else
  224. return ( -1.0 );
  225. }
  226. /*******************************************************************************
  227. * Is 1.0 < |x| < 2.0^52? *
  228. *******************************************************************************/
  229. if ( target )
  230. { // positive x
  231. y = ( x + twoTo52 ) - twoTo52; // round at binary point
  232. if ( y == x ) // exact case
  233. return ( x );
  234. z = x + 0.5; // inexact case
  235. y = ( z + twoTo52 ) - twoTo52; // round at binary point
  236. if ( y > z )
  237. return ( y - 1.0 );
  238. else
  239. return ( y );
  240. }
  241. /*******************************************************************************
  242. * Is x < 0? *
  243. *******************************************************************************/
  244. else
  245. {
  246. y = ( x - twoTo52 ) + twoTo52; // round at binary point
  247. if ( y == x )
  248. return ( x );
  249. z = x - 0.5;
  250. y = ( z - twoTo52 ) + twoTo52; // round at binary point
  251. if ( y < z )
  252. return ( y + 1.0 );
  253. else
  254. return ( y );
  255. }
  256. }
  257. /*******************************************************************************
  258. * |x| >= 2.0^52 or x is a NaN. *
  259. *******************************************************************************/
  260. return ( x );
  261. }
  262. /*******************************************************************************
  263. * *
  264. * The function roundtol converts its double argument to integral format *
  265. * according to the "add half to the magnitude and chop" rounding mode of *
  266. * Pascal's Round function and FORTRAN's NINT function. This conversion *
  267. * signals invalid if the argument is a NaN or the rounded intermediate *
  268. * result is out of range of the destination long int format, and it *
  269. * delivers an unspecified result in this case. This function signals *
  270. * inexact if the rounded result is within range of the long int format but *
  271. * unequal to the operand. *
  272. * *
  273. *******************************************************************************/
  274. long int roundtol ( double x )
  275. {
  276. register double y, z;
  277. DblInHex argument, OldEnvironment;
  278. register unsigned long int xhi;
  279. register long int target;
  280. const DblInHex kTZ = {{ 0x0, 0x1 }};
  281. const DblInHex kUP = {{ 0x0, 0x2 }};
  282. argument.dbl = x;
  283. xhi = argument.words.hi & 0x7ffffffful; // high 32 bits of x
  284. target = ( argument.words.hi < signMask ); // flag positive sign
  285. if ( xhi > 0x41e00000ul )
  286. /*******************************************************************************
  287. * Is x is out of long range or NaN? *
  288. *******************************************************************************/
  289. {
  290. asm ("mffs %0" : "=f" (OldEnvironment.dbl)); // get environment
  291. OldEnvironment.words.lo |= SET_INVALID;
  292. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  293. if ( target ) // pin result
  294. return ( LONG_MAX );
  295. else
  296. return ( LONG_MIN );
  297. }
  298. if ( target )
  299. /*******************************************************************************
  300. * Is sign of x is "+"? *
  301. *******************************************************************************/
  302. {
  303. if ( x < 2147483647.5 )
  304. /*******************************************************************************
  305. * x is in the range of a long. *
  306. *******************************************************************************/
  307. {
  308. y = ( x + doubleToLong ) - doubleToLong; // round at binary point
  309. if ( y != x )
  310. { // inexact case
  311. asm ("mffs %0" : "=f" (OldEnvironment.dbl)); // save environment
  312. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( kTZ.dbl )); // truncate rounding
  313. z = x + 0.5; // truncate x + 0.5
  314. argument.dbl = z + doubleToLong;
  315. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  316. return ( ( long ) argument.words.lo );
  317. }
  318. argument.dbl = y + doubleToLong; // force result into argument.words.lo
  319. return ( ( long ) argument.words.lo ); // return long result
  320. }
  321. /*******************************************************************************
  322. * Rounded positive x is out of the range of a long. *
  323. *******************************************************************************/
  324. asm ("mffs %0" : "=f" (OldEnvironment.dbl));
  325. OldEnvironment.words.lo |= SET_INVALID;
  326. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  327. return ( LONG_MAX ); // return pinned result
  328. }
  329. /*******************************************************************************
  330. * x < 0.0 and may or may not be out of the range of a long. *
  331. *******************************************************************************/
  332. if ( x > -2147483648.5 )
  333. /*******************************************************************************
  334. * x is in the range of a long. *
  335. *******************************************************************************/
  336. {
  337. y = ( x + doubleToLong ) - doubleToLong; // round at binary point
  338. if ( y != x )
  339. { // inexact case
  340. asm ("mffs %0" : "=f" (OldEnvironment.dbl)); // save environment
  341. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( kUP.dbl )); // round up
  342. z = x - 0.5; // truncate x - 0.5
  343. argument.dbl = z + doubleToLong;
  344. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  345. return ( ( long ) argument.words.lo );
  346. }
  347. argument.dbl = y + doubleToLong;
  348. return ( ( long ) argument.words.lo ); // return long result
  349. }
  350. /*******************************************************************************
  351. * Rounded negative x is out of the range of a long. *
  352. *******************************************************************************/
  353. asm ("mffs %0" : "=f" (OldEnvironment.dbl));
  354. OldEnvironment.words.lo |= SET_INVALID;
  355. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  356. return ( LONG_MIN ); // return pinned result
  357. }
  358. /*******************************************************************************
  359. * *
  360. * The function trunc truncates its double argument to integral value *
  361. * and returns the result in double format. This function signals *
  362. * inexact if an ordered return value is not equal to the operand. *
  363. * *
  364. *******************************************************************************/
  365. double trunc ( double x )
  366. {
  367. DblInHex argument,OldEnvironment;
  368. register double y;
  369. register unsigned long int xhi;
  370. register long int target;
  371. argument.dbl = x;
  372. xhi = argument.words.hi & 0x7fffffffUL; // xhi <- high half of |x|
  373. target = ( argument.words.hi < signMask ); // flag positive sign
  374. if ( xhi < 0x43300000ul )
  375. /*******************************************************************************
  376. * Is |x| < 2.0^53? *
  377. *******************************************************************************/
  378. {
  379. if ( xhi < 0x3ff00000ul )
  380. /*******************************************************************************
  381. * Is |x| < 1.0? *
  382. *******************************************************************************/
  383. {
  384. if ( ( xhi | argument.words.lo ) != 0ul )
  385. { // raise deserved INEXACT
  386. asm ("mffs %0" : "=f" (OldEnvironment.dbl));
  387. OldEnvironment.words.lo |= 0x02000000ul;
  388. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment.dbl ));
  389. }
  390. if ( target ) // return properly signed zero
  391. return ( 0.0 );
  392. else
  393. return ( -0.0 );
  394. }
  395. /*******************************************************************************
  396. * Is 1.0 < |x| < 2.0^52? *
  397. *******************************************************************************/
  398. if ( target )
  399. {
  400. y = ( x + twoTo52 ) - twoTo52; // round at binary point
  401. if ( y > x )
  402. return ( y - 1.0 );
  403. else
  404. return ( y );
  405. }
  406. else
  407. {
  408. y = ( x - twoTo52 ) + twoTo52; // round at binary point.
  409. if ( y < x )
  410. return ( y + 1.0 );
  411. else
  412. return ( y );
  413. }
  414. }
  415. /*******************************************************************************
  416. * Is |x| >= 2.0^52 or x is a NaN. *
  417. *******************************************************************************/
  418. return ( x );
  419. }
  420. /*******************************************************************************
  421. * The modf family of functions separate a floating-point number into its *
  422. * fractional and integral parts, returning the fractional part and writing *
  423. * the integral part in floating-point format to the object pointed to by a *
  424. * pointer argument. If the input argument is integral or infinite in *
  425. * value, the return value is a zero with the sign of the input argument. *
  426. * The modf family of functions raises no floating-point exceptions. older *
  427. * implemenation set the INVALID flag due to signaling NaN input. *
  428. * *
  429. *******************************************************************************/
  430. /*******************************************************************************
  431. * modf is the double implementation. *
  432. *******************************************************************************/
  433. double modf ( double x, double *iptr )
  434. {
  435. register double OldEnvironment, xtrunc;
  436. register unsigned long int xHead, signBit;
  437. DblInHex argument;
  438. argument.dbl = x;
  439. xHead = argument.words.hi & 0x7ffffffful; // |x| high bit pattern
  440. signBit = ( argument.words.hi & 0x80000000ul ); // isolate sign bit
  441. if (xHead == 0x7ff81fe0)
  442. signBit = signBit | 0;
  443. if ( xHead < 0x43300000ul )
  444. /*******************************************************************************
  445. * Is |x| < 2.0^53? *
  446. *******************************************************************************/
  447. {
  448. if ( xHead < 0x3ff00000ul )
  449. /*******************************************************************************
  450. * Is |x| < 1.0? *
  451. *******************************************************************************/
  452. {
  453. argument.words.hi = signBit; // truncate to zero
  454. argument.words.lo = 0ul;
  455. *iptr = argument.dbl;
  456. return ( x );
  457. }
  458. /*******************************************************************************
  459. * Is 1.0 < |x| < 2.0^52? *
  460. *******************************************************************************/
  461. asm ("mffs %0" : "=f" (OldEnvironment)); // save environment
  462. // round toward zero
  463. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( TOWARDZERO.dbl ));
  464. if ( signBit == 0ul ) // truncate to integer
  465. xtrunc = ( x + twoTo52 ) - twoTo52;
  466. else
  467. xtrunc = ( x - twoTo52 ) + twoTo52;
  468. // restore caller's env
  469. asm ("mtfsf 255,%0" : /*NULLOUT*/ : /*IN*/ "f" ( OldEnvironment ));
  470. *iptr = xtrunc; // store integral part
  471. if ( x != xtrunc ) // nonzero fraction
  472. return ( x - xtrunc );
  473. else
  474. { // zero with x's sign
  475. argument.words.hi = signBit;
  476. argument.words.lo = 0ul;
  477. return ( argument.dbl );
  478. }
  479. }
  480. *iptr = x; // x is integral or NaN
  481. if ( x != x ) // NaN is returned
  482. return x;
  483. else
  484. { // zero with x's sign
  485. argument.words.hi = signBit;
  486. argument.words.lo = 0ul;
  487. return ( argument.dbl );
  488. }
  489. }