Raymond Toy pushed to branch issue-474-print-parse-hex-floats at cmucl / cmucl

Commits:

4 changed files:

Changes:

  • src/code/ext-code.lisp
    1
    +;;; -*- Log: code.log; Package: Extensions -*-
    
    2
    +;;;
    
    3
    +;;; **********************************************************************
    
    4
    +;;; This code was written as part of the CMU Common Lisp project at
    
    5
    +;;; Carnegie Mellon University, and has been placed in the public domain.
    
    6
    +;;;
    
    7
    +(ext:file-comment
    
    8
    +  "$Header: src/code/extensions.lisp $")
    
    9
    +;;;
    
    10
    +;;;
    
    11
    +;;; **********************************************************************
    
    12
    +;;;
    
    13
    +;;; Spice Lisp extensions to the language.
    
    14
    +;;;
    
    15
    +;;; These extensions are compiled natively instead of byte-compiled
    
    16
    +;;; like the code in code/extensions.lisp.
    
    17
    +;;;
    
    18
    +;;; **********************************************************************
    
    19
    +(in-package "EXTENSIONS")
    
    20
    +
    
    21
    +(intl:textdomain "cmucl")
    
    22
    +
    
    23
    +
    
    24
    +;;; C-style hex float printer and parser
    
    25
    +(defun print-hex-single-float (val &optional force-sign)
    
    26
    +  (let* ((bits (kernel:single-float-bits val))
    
    27
    +         (u-bits (ldb (byte 32 0) bits))
    
    28
    +         (sign-bit (ldb (byte 1 31) u-bits))
    
    29
    +         (biased-exp (ldb (byte 8 23) u-bits))
    
    30
    +         (fraction (ldb (byte 23 0) u-bits))
    
    31
    +         (sign-str (cond ((= sign-bit 1) "-") (force-sign "+") (t ""))))
    
    32
    +    (cond 
    
    33
    +      ((= biased-exp 255) (if (zerop fraction) (format nil "~Ainf" sign-str) "nan"))
    
    34
    +      ((and (zerop biased-exp) (zerop fraction)) (format nil "~A0x0.000000p+0" sign-str))
    
    35
    +      ((zerop biased-exp) (format nil "~A0x0.~6,'0xp-126" sign-str fraction))
    
    36
    +      (t (let ((exponent (- biased-exp 127)))
    
    37
    +           (format nil "~A0x1.~6,'0xp~:[~;+~]~D" sign-str fraction (not (minusp exponent)) exponent))))))
    
    38
    +
    
    39
    +(defun print-hex-double-float (val &optional force-sign)
    
    40
    +  (multiple-value-bind (hi lo) (kernel:double-float-bits val)
    
    41
    +    (let* ((u-hi (ldb (byte 32 0) hi))
    
    42
    +           (sign-bit (ldb (byte 1 31) u-hi))
    
    43
    +           (biased-exp (ldb (byte 11 20) u-hi))
    
    44
    +           (fraction (logior (ash (ldb (byte 20 0) u-hi) 32) lo))
    
    45
    +           (sign-str (cond ((= sign-bit 1) "-") (force-sign "+") (t ""))))
    
    46
    +      (cond 
    
    47
    +        ((= biased-exp #x7FF) (if (zerop fraction) (format nil "~Ainf" sign-str) "nan"))
    
    48
    +        ((and (zerop biased-exp) (zerop fraction)) (format nil "~A0x0.0000000000000p+0" sign-str))
    
    49
    +        ((zerop biased-exp) (format nil "~A0x0.~13,'0xp-1022" sign-str fraction))
    
    50
    +        (t (let ((exponent (- biased-exp 1023)))
    
    51
    +             (format nil "~A0x1.~13,'0xp~:[~;+~]~D" sign-str fraction (not (minusp exponent)) exponent)))))))
    
    52
    +
    
    53
    +#+nil
    
    54
    +(defun print-hex-single-float (val &optional force-sign)
    
    55
    +  "Converts a single-float to a C-style hex string (32-bit)."
    
    56
    +  (let* ((bits (kernel:single-float-bits val))
    
    57
    +         (u-bits (ldb (byte 32 0) bits))
    
    58
    +         (sign-bit (ldb (byte 1 31) u-bits))
    
    59
    +         (biased-exp (ldb (byte 8 23) u-bits))
    
    60
    +         (fraction (ldb (byte 23 0) u-bits))
    
    61
    +         (sign-str (cond ((= sign-bit 1)
    
    62
    +                          "-")
    
    63
    +                         (force-sign
    
    64
    +                          "+")
    
    65
    +                         (t
    
    66
    +                          ""))))
    
    67
    +    (cond 
    
    68
    +      ((= biased-exp 255)
    
    69
    +       (if (zerop fraction)
    
    70
    +           (format nil "~Ainf" sign-str)
    
    71
    +           "nan"))
    
    72
    +      ((and (zerop biased-exp)
    
    73
    +            (zerop fraction))
    
    74
    +       (format nil "~A0x0.000000p+0" sign-str))
    
    75
    +      ((zerop biased-exp)
    
    76
    +       (let ((*print-case* :downcase))
    
    77
    +	 (format nil "~A0x0.~6,'0xp-126" sign-str fraction)))
    
    78
    +      (t
    
    79
    +       (let ((*print-case* :downcase)
    
    80
    +	     (exponent (- biased-exp 127)))
    
    81
    +         (format nil "~A0x1.~6,'0xp~:[~;+~]~D"
    
    82
    +                 sign-str fraction (not (minusp exponent)) exponent))))))
    
    83
    +
    
    84
    +#+nil
    
    85
    +(defun print-hex-double-float (val &optional force-sign)
    
    86
    +  "Converts a double-float to a C-style hex string (64-bit)."
    
    87
    +  (multiple-value-bind (hi lo)
    
    88
    +      (kernel:double-float-bits val)
    
    89
    +    (let* ((u-hi (ldb (byte 32 0) hi))
    
    90
    +           (sign-bit (ldb (byte 1 31) u-hi))
    
    91
    +           (biased-exp (ldb (byte 11 20) u-hi))
    
    92
    +           (fraction (logior (ash (ldb (byte 20 0) u-hi) 32) lo))
    
    93
    +           (sign-str (cond ((= sign-bit 1)
    
    94
    +                            "-")
    
    95
    +                           (force-sign "+")
    
    96
    +                           (t ""))))
    
    97
    +      (cond 
    
    98
    +        ((= biased-exp #x7FF)
    
    99
    +         (if (zerop fraction)
    
    100
    +             (format nil "~Ainf" sign-str)
    
    101
    +             "nan"))
    
    102
    +        ((and (zerop biased-exp)
    
    103
    +              (zerop fraction))
    
    104
    +         (format nil "~A0x0.0000000000000p+0" sign-str))
    
    105
    +        ((zerop biased-exp)
    
    106
    +	 (let ((*print-case* :downcase))
    
    107
    +           (format nil "~A0x0.~13,'0xp-1022" sign-str fraction)))
    
    108
    +        (t
    
    109
    +         (let ((*print-case* :downcase)
    
    110
    +	       (exponent (- biased-exp 1023)))
    
    111
    +           (format nil "~A0x1.~13,'0xp~:[~;+~]~D"
    
    112
    +                   sign-str fraction (not (minusp exponent)) exponent)))))))
    
    113
    +
    
    114
    +;;; PRINT-HEX-FLOAT  -- Public
    
    115
    +;;;
    
    116
    +;;; Return a string representing a single and double-floats in C-style
    
    117
    +;;; hex format.
    
    118
    +(defun print-hex-float (float)
    
    119
    +  "Convert FLOAT to C-style hex string.  Infinities are printed as \"-inf\"
    
    120
    +  and \"inf\".  NaN is printed as \"nan\"."
    
    121
    +  (declare (float float))
    
    122
    +  (etypecase float
    
    123
    +    (single-float (print-hex-single-float float))
    
    124
    +    (double-float (print-hex-double-float float))))
    
    125
    +
    
    126
    +;;; FORMAT-HEX-FLOAT -- Public
    
    127
    +;;;
    
    128
    +;;; Function that can be used in a FORMAT ~/
    
    129
    +(defun format-hex-float (stream val &optional colon-p at-p &rest params)
    
    130
    +  "Format ~/ directive supporting @ (sign) modifier for single/double floats."
    
    131
    +  (declare (ignore colon-p params))
    
    132
    +  (write-string
    
    133
    +   (typecase val
    
    134
    +     (single-float (print-hex-single-float val at-p))
    
    135
    +     (double-float (print-hex-double-float val at-p))
    
    136
    +     (t (format nil "~A" val)))
    
    137
    +   stream))
    
    138
    +
    
    139
    +;;; PARSE-HEX-FLOAT -- Public
    
    140
    +;;;
    
    141
    +;;; Parse a C-style float hex strings.  Always returns a double-float.
    
    142
    +;;; Error-checking is enabled for malformed strings.
    
    143
    +(define-condition hex-parse-error (error)
    
    144
    +  ((text :initarg :text :reader hex-parse-error-text)
    
    145
    +   (message :initarg :message :reader hex-parse-error-message))
    
    146
    +  (:report (lambda (c s)
    
    147
    +             (format s "Hex float parse error in ~S: ~A" 
    
    148
    +                     (hex-parse-error-text c) (hex-parse-error-message c)))))
    
    149
    +
    
    150
    +#+nil
    
    151
    +(defun parse-hex-float (str)
    
    152
    +  "Parses hex strings by converting the significand to a float, then scaling."
    
    153
    +  (let* ((str (string-trim '(#\Space #\Tab #\Newline #\Return) (string-downcase str)))
    
    154
    +         (len (length str)))
    
    155
    +    (when (zerop len) (error 'hex-parse-error :text str :message "Empty string"))
    
    156
    +    (let* ((has-sign (or (char= (char str 0) #\-) (char= (char str 0) #\+)))
    
    157
    +           (sign (if (and has-sign (char= (char str 0) #\-)) -1 1))
    
    158
    +           (start (if has-sign 1 0)))
    
    159
    +      (cond
    
    160
    +        ((string= str "inf" :start1 start) 
    
    161
    +         (if (= sign 1) double-float-positive-infinity double-float-negative-infinity))
    
    162
    +        ((string= str "nan" :start1 start) :nan)
    
    163
    +        (t
    
    164
    +         (unless (and (<= (+ start 2) len) (string= str "0x" :start1 start :end1 (+ start 2)))
    
    165
    +           (error 'hex-parse-error :text str :message "Missing '0x' prefix"))
    
    166
    +         (let ((p-pos (position #\p str :start start)))
    
    167
    +           (unless p-pos (error 'hex-parse-error :text str :message "Missing exponent 'p'"))
    
    168
    +           
    
    169
    +           ;; Check for internal whitespace
    
    170
    +           (loop for i from start below len
    
    171
    +                 when (member (char str i) '(#\Space #\Tab #\Newline #\Return))
    
    172
    +                 do (error 'hex-parse-error :text str :message "Internal whitespace detected"))
    
    173
    +
    
    174
    +           (let* ((sig-start (+ start 2))
    
    175
    +                  (dot-pos (position #\. str :start sig-start :end p-pos))
    
    176
    +                  (exp-start (1+ p-pos)))
    
    177
    +             
    
    178
    +             (handler-case
    
    179
    +                 (let* ((frac-hex-len (if dot-pos (- p-pos (1+ dot-pos)) 0))
    
    180
    +                        ;; 1. Combine leading and trailing into one large integer
    
    181
    +                        (significand-int 
    
    182
    +                         (if (null dot-pos)
    
    183
    +                             (parse-integer str :start sig-start :end p-pos :radix 16)
    
    184
    +                             (let ((leading (if (= sig-start dot-pos) 0 
    
    185
    +                                                (parse-integer str :start sig-start :end dot-pos :radix 16)))
    
    186
    +                                   (trailing (if (= (1+ dot-pos) p-pos) 0
    
    187
    +                                                 (parse-integer str :start (1+ dot-pos) :end p-pos :radix 16))))
    
    188
    +                               (+ (ash leading (* 4 frac-hex-len)) trailing))))
    
    189
    +                        ;; 2. Parse decimal exponent
    
    190
    +                        (raw-exponent (parse-integer str :start exp-start :end len))
    
    191
    +                        ;; 3. Handle the "cliff" logic for 0x0. vs 0x1.
    
    192
    +                        (starts-with-zero (char= (char str sig-start) #\0))
    
    193
    +                        (actual-exponent (if (and starts-with-zero (not (zerop significand-int)))
    
    194
    +                                             -1022
    
    195
    +                                             raw-exponent)))
    
    196
    +                   
    
    197
    +                   ;; 4. Convert integer to float and scale by (exponent - fractional bits)
    
    198
    +                   ;; scale-float is bit-exact for binary scaling.
    
    199
    +                   (* sign (scale-float (float significand-int 1.0d0) 
    
    200
    +                                        (- actual-exponent (* 4 frac-hex-len)))))
    
    201
    +               (error (c) (error 'hex-parse-error :text str :message (format nil "~A" c)))))))))))
    
    202
    +
    
    203
    +(defun parse-hex-float (str)
    
    204
    +  "Parses C-style hex strings via an exact rational. Strictly validates digit presence."
    
    205
    +  (let* ((str (string-trim '(#\Space #\Tab #\Newline #\Return) (string-downcase str)))
    
    206
    +         (len (length str)))
    
    207
    +    (when (zerop len) (error 'hex-parse-error :text str :message "Empty string"))
    
    208
    +    (let* ((has-sign (or (char= (char str 0) #\-) (char= (char str 0) #\+)))
    
    209
    +           (sign (if (and has-sign (char= (char str 0) #\-)) -1 1))
    
    210
    +           (start (if has-sign 1 0)))
    
    211
    +      (cond
    
    212
    +        ((string= str "inf" :start1 start) 
    
    213
    +         (if (= sign 1) double-float-positive-infinity double-float-negative-infinity))
    
    214
    +        ((string= str "nan" :start1 start) :nan)
    
    215
    +        (t
    
    216
    +         (unless (and (<= (+ start 2) len) (string= str "0x" :start1 start :end1 (+ start 2)))
    
    217
    +           (error 'hex-parse-error :text str :message "Missing '0x' prefix"))
    
    218
    +         (let ((p-pos (position #\p str :start start)))
    
    219
    +           (unless p-pos (error 'hex-parse-error :text str :message "Missing exponent 'p'"))
    
    220
    +           
    
    221
    +           (loop for i from start below len
    
    222
    +                 when (member (char str i) '(#\Space #\Tab #\Newline #\Return))
    
    223
    +                 do (error 'hex-parse-error :text str :message "Internal whitespace detected"))
    
    224
    +
    
    225
    +           (let* ((sig-start (+ start 2))
    
    226
    +                  (dot-pos (position #\. str :start sig-start :end p-pos))
    
    227
    +                  (exp-start (1+ p-pos))
    
    228
    +                  ;; Strict Validation: Ensure there is at least one digit in the significand
    
    229
    +                  (has-leading (and (not (eql sig-start dot-pos)) (not (eql sig-start p-pos))))
    
    230
    +                  (has-trailing (and dot-pos (not (eql (1+ dot-pos) p-pos)))))
    
    231
    +             
    
    232
    +             (unless (or has-leading has-trailing)
    
    233
    +               (error 'hex-parse-error :text str :message "No hex digits in significand"))
    
    234
    +             
    
    235
    +             (handler-case
    
    236
    +                 (let* ((frac-hex-len (if dot-pos (- p-pos (1+ dot-pos)) 0))
    
    237
    +                        (significand-int 
    
    238
    +                         (if (null dot-pos)
    
    239
    +                             (parse-integer str :start sig-start :end p-pos :radix 16)
    
    240
    +                             (let ((leading (if (not has-leading) 0 
    
    241
    +                                                (parse-integer str :start sig-start :end dot-pos :radix 16)))
    
    242
    +                                   (trailing (if (not has-trailing) 0
    
    243
    +                                                 (parse-integer str :start (1+ dot-pos) :end p-pos :radix 16))))
    
    244
    +                               (+ (ash leading (* 4 frac-hex-len)) trailing))))
    
    245
    +                        (raw-exponent (parse-integer str :start exp-start :end len))
    
    246
    +                        ;; significand * 2^(exp - 4*frac_len)
    
    247
    +                        (rational-val (* significand-int 
    
    248
    +                                         (expt 2 (- raw-exponent (* 4 frac-hex-len))))))
    
    249
    +                   (* sign (float rational-val 1.0d0)))
    
    250
    +               (error (c) (error 'hex-parse-error :text str :message (format nil "~A" c)))))))))))

  • src/code/extensions.lisp
    ... ... @@ -725,304 +725,6 @@
    725 725
     	 (when ,dirname
    
    726 726
     	   (delete-directory ,dirname :recursive t))))))
    
    727 727
     
    
    728
    -;;; C-style hex float printer and parser
    
    729
    -(defun print-hex-single-float (val &optional force-sign)
    
    730
    -  (let* ((bits (kernel:single-float-bits val))
    
    731
    -         (u-bits (ldb (byte 32 0) bits))
    
    732
    -         (sign-bit (ldb (byte 1 31) u-bits))
    
    733
    -         (biased-exp (ldb (byte 8 23) u-bits))
    
    734
    -         (fraction (ldb (byte 23 0) u-bits))
    
    735
    -         (sign-str (cond ((= sign-bit 1) "-") (force-sign "+") (t ""))))
    
    736
    -    (cond 
    
    737
    -      ((= biased-exp 255) (if (zerop fraction) (format nil "~Ainf" sign-str) "nan"))
    
    738
    -      ((and (zerop biased-exp) (zerop fraction)) (format nil "~A0x0.000000p+0" sign-str))
    
    739
    -      ((zerop biased-exp) (format nil "~A0x0.~6,'0xp-126" sign-str fraction))
    
    740
    -      (t (let ((exponent (- biased-exp 127)))
    
    741
    -           (format nil "~A0x1.~6,'0xp~:[~;+~]~D" sign-str fraction (not (minusp exponent)) exponent))))))
    
    742
    -
    
    743
    -(defun print-hex-double-float (val &optional force-sign)
    
    744
    -  (multiple-value-bind (hi lo) (kernel:double-float-bits val)
    
    745
    -    (let* ((u-hi (ldb (byte 32 0) hi))
    
    746
    -           (sign-bit (ldb (byte 1 31) u-hi))
    
    747
    -           (biased-exp (ldb (byte 11 20) u-hi))
    
    748
    -           (fraction (logior (ash (ldb (byte 20 0) u-hi) 32) lo))
    
    749
    -           (sign-str (cond ((= sign-bit 1) "-") (force-sign "+") (t ""))))
    
    750
    -      (cond 
    
    751
    -        ((= biased-exp #x7FF) (if (zerop fraction) (format nil "~Ainf" sign-str) "nan"))
    
    752
    -        ((and (zerop biased-exp) (zerop fraction)) (format nil "~A0x0.0000000000000p+0" sign-str))
    
    753
    -        ((zerop biased-exp) (format nil "~A0x0.~13,'0xp-1022" sign-str fraction))
    
    754
    -        (t (let ((exponent (- biased-exp 1023)))
    
    755
    -             (format nil "~A0x1.~13,'0xp~:[~;+~]~D" sign-str fraction (not (minusp exponent)) exponent)))))))
    
    756
    -
    
    757
    -#+nil
    
    758
    -(defun print-hex-single-float (val &optional force-sign)
    
    759
    -  "Converts a single-float to a C-style hex string (32-bit)."
    
    760
    -  (let* ((bits (kernel:single-float-bits val))
    
    761
    -         (u-bits (ldb (byte 32 0) bits))
    
    762
    -         (sign-bit (ldb (byte 1 31) u-bits))
    
    763
    -         (biased-exp (ldb (byte 8 23) u-bits))
    
    764
    -         (fraction (ldb (byte 23 0) u-bits))
    
    765
    -         (sign-str (cond ((= sign-bit 1)
    
    766
    -                          "-")
    
    767
    -                         (force-sign
    
    768
    -                          "+")
    
    769
    -                         (t
    
    770
    -                          ""))))
    
    771
    -    (cond 
    
    772
    -      ((= biased-exp 255)
    
    773
    -       (if (zerop fraction)
    
    774
    -           (format nil "~Ainf" sign-str)
    
    775
    -           "nan"))
    
    776
    -      ((and (zerop biased-exp)
    
    777
    -            (zerop fraction))
    
    778
    -       (format nil "~A0x0.000000p+0" sign-str))
    
    779
    -      ((zerop biased-exp)
    
    780
    -       (let ((*print-case* :downcase))
    
    781
    -	 (format nil "~A0x0.~6,'0xp-126" sign-str fraction)))
    
    782
    -      (t
    
    783
    -       (let ((*print-case* :downcase)
    
    784
    -	     (exponent (- biased-exp 127)))
    
    785
    -         (format nil "~A0x1.~6,'0xp~:[~;+~]~D"
    
    786
    -                 sign-str fraction (not (minusp exponent)) exponent))))))
    
    787
    -
    
    788
    -#+nil
    
    789
    -(defun print-hex-double-float (val &optional force-sign)
    
    790
    -  "Converts a double-float to a C-style hex string (64-bit)."
    
    791
    -  (multiple-value-bind (hi lo)
    
    792
    -      (kernel:double-float-bits val)
    
    793
    -    (let* ((u-hi (ldb (byte 32 0) hi))
    
    794
    -           (sign-bit (ldb (byte 1 31) u-hi))
    
    795
    -           (biased-exp (ldb (byte 11 20) u-hi))
    
    796
    -           (fraction (logior (ash (ldb (byte 20 0) u-hi) 32) lo))
    
    797
    -           (sign-str (cond ((= sign-bit 1)
    
    798
    -                            "-")
    
    799
    -                           (force-sign "+")
    
    800
    -                           (t ""))))
    
    801
    -      (cond 
    
    802
    -        ((= biased-exp #x7FF)
    
    803
    -         (if (zerop fraction)
    
    804
    -             (format nil "~Ainf" sign-str)
    
    805
    -             "nan"))
    
    806
    -        ((and (zerop biased-exp)
    
    807
    -              (zerop fraction))
    
    808
    -         (format nil "~A0x0.0000000000000p+0" sign-str))
    
    809
    -        ((zerop biased-exp)
    
    810
    -	 (let ((*print-case* :downcase))
    
    811
    -           (format nil "~A0x0.~13,'0xp-1022" sign-str fraction)))
    
    812
    -        (t
    
    813
    -         (let ((*print-case* :downcase)
    
    814
    -	       (exponent (- biased-exp 1023)))
    
    815
    -           (format nil "~A0x1.~13,'0xp~:[~;+~]~D"
    
    816
    -                   sign-str fraction (not (minusp exponent)) exponent)))))))
    
    817
    -
    
    818
    -;;; PRINT-HEX-FLOAT  -- Public
    
    819
    -;;;
    
    820
    -;;; Return a string representing a single and double-floats in C-style
    
    821
    -;;; hex format.
    
    822
    -(defun print-hex-float (float)
    
    823
    -  "Convert FLOAT to C-style hex string.  Infinities are printed as \"-inf\"
    
    824
    -  and \"inf\".  NaN is printed as \"nan\"."
    
    825
    -  (declare (float float))
    
    826
    -  (etypecase float
    
    827
    -    (single-float (print-hex-single-float float))
    
    828
    -    (double-float (print-hex-double-float float))))
    
    829
    -
    
    830
    -;;; FORMAT-HEX-FLOAT -- Public
    
    831
    -;;;
    
    832
    -;;; Function that can be used in a FORMAT ~/
    
    833
    -(defun format-hex-float (stream val &optional colon-p at-p &rest params)
    
    834
    -  "Format ~/ directive supporting @ (sign) modifier for single/double floats."
    
    835
    -  (declare (ignore colon-p params))
    
    836
    -  (write-string
    
    837
    -   (typecase val
    
    838
    -     (single-float (print-hex-single-float val at-p))
    
    839
    -     (double-float (print-hex-double-float val at-p))
    
    840
    -     (t (format nil "~A" val)))
    
    841
    -   stream))
    
    842
    -
    
    843
    -;;; PARSE-HEX-FLOAT -- Public
    
    844
    -;;;
    
    845
    -;;; Parse a C-style float hex strings.  Always returns a double-float.
    
    846
    -;;; Error-checking is enabled for malformed strings.
    
    847
    -(define-condition hex-parse-error (error)
    
    848
    -  ((text :initarg :text :reader hex-parse-error-text)
    
    849
    -   (message :initarg :message :reader hex-parse-error-message))
    
    850
    -  (:report (lambda (c s)
    
    851
    -             (format s "Hex float parse error in ~S: ~A" 
    
    852
    -                     (hex-parse-error-text c) (hex-parse-error-message c)))))
    
    853
    -
    
    854
    -(defun parse-hex-float (str)
    
    855
    -  "Parses C-style hex strings by converting to an exact rational, then to double-float."
    
    856
    -  (let* ((str (string-trim '(#\Space #\Tab #\Newline #\Return) (string-downcase str)))
    
    857
    -         (len (length str)))
    
    858
    -    (when (zerop len) (error 'hex-parse-error :text str :message "Empty string"))
    
    859
    -    (let* ((has-sign (or (char= (char str 0) #\-) (char= (char str 0) #\+)))
    
    860
    -           (sign (if (and has-sign (char= (char str 0) #\-)) -1 1))
    
    861
    -           (start (if has-sign 1 0)))
    
    862
    -      (cond
    
    863
    -        ((string= str "inf" :start1 start) 
    
    864
    -         (if (= sign 1) double-float-positive-infinity double-float-negative-infinity))
    
    865
    -        ((string= str "nan" :start1 start) :nan)
    
    866
    -        (t
    
    867
    -         (unless (and (<= (+ start 2) len) (string= str "0x" :start1 start :end1 (+ start 2)))
    
    868
    -           (error 'hex-parse-error :text str :message "Missing '0x' prefix"))
    
    869
    -         (let ((p-pos (position #\p str :start start)))
    
    870
    -           (unless p-pos (error 'hex-parse-error :text str :message "Missing exponent 'p'"))
    
    871
    -           
    
    872
    -           (loop for i from start below len
    
    873
    -                 when (member (char str i) '(#\Space #\Tab #\Newline #\Return))
    
    874
    -                 do (error 'hex-parse-error :text str :message "Internal whitespace detected"))
    
    875
    -
    
    876
    -           (let* ((sig-start (+ start 2))
    
    877
    -                  (dot-pos (position #\. str :start sig-start :end p-pos))
    
    878
    -                  (exp-start (1+ p-pos)))
    
    879
    -             (when (or (= sig-start p-pos) 
    
    880
    -                       (and dot-pos (= (1+ sig-start) p-pos) (= sig-start dot-pos)))
    
    881
    -               (error 'hex-parse-error :text str :message "No hex digits in significand"))
    
    882
    -             
    
    883
    -             (handler-case
    
    884
    -                 (let* ((frac-hex-len (if dot-pos (- p-pos (1+ dot-pos)) 0))
    
    885
    -                        ;; 1. Parse significand as one large integer
    
    886
    -                        (significand-int 
    
    887
    -                         (if (null dot-pos)
    
    888
    -                             (parse-integer str :start sig-start :end p-pos :radix 16)
    
    889
    -                             (let ((leading (if (= sig-start dot-pos) 0 
    
    890
    -                                                (parse-integer str :start sig-start :end dot-pos :radix 16)))
    
    891
    -                                   (trailing (if (= (1+ dot-pos) p-pos) 0
    
    892
    -                                                 (parse-integer str :start (1+ dot-pos) :end p-pos :radix 16))))
    
    893
    -                               (+ (ash leading (* 4 frac-hex-len)) trailing))))
    
    894
    -                        ;; 2. Parse exponent
    
    895
    -                        (raw-exponent (parse-integer str :start exp-start :end len))
    
    896
    -                        ;; 3. Build exact rational: significand / 16^frac-len * 2^exponent
    
    897
    -                        (rational-val (* significand-int 
    
    898
    -                                         (expt 2 (- raw-exponent (* 4 frac-hex-len))))))
    
    899
    -                   ;; 4. Coerce to double-float
    
    900
    -                   (* sign (float rational-val 1.0d0)))
    
    901
    -               (error (c) (error 'hex-parse-error :text str :message (format nil "~A" c)))))))))))
    
    902
    -
    
    903
    -#+nil
    
    904
    -(defun parse-hex-float (str)
    
    905
    -  "Parses C-style hex strings into double-floats using robust integer scaling."
    
    906
    -  (let* ((str (string-trim '(#\Space #\Tab #\Newline #\Return) (string-downcase str)))
    
    907
    -         (len (length str)))
    
    908
    -    (when (zerop len) (error 'hex-parse-error :text str :message "Empty string"))
    
    909
    -    (let* ((has-sign (or (char= (char str 0) #\-) (char= (char str 0) #\+)))
    
    910
    -           (sign (if (and has-sign (char= (char str 0) #\-)) -1 1))
    
    911
    -           (start (if has-sign 1 0)))
    
    912
    -      (cond
    
    913
    -        ((string= str "inf" :start1 start) 
    
    914
    -         (if (= sign 1) double-float-positive-infinity double-float-negative-infinity))
    
    915
    -        ((string= str "nan" :start1 start) :nan)
    
    916
    -        (t
    
    917
    -         (unless (and (<= (+ start 2) len) (string= str "0x" :start1 start :end1 (+ start 2)))
    
    918
    -           (error 'hex-parse-error :text str :message "Missing '0x' prefix"))
    
    919
    -         (let ((p-pos (position #\p str :start start)))
    
    920
    -           (unless p-pos (error 'hex-parse-error :text str :message "Missing exponent 'p'"))
    
    921
    -           
    
    922
    -           (loop for i from start below len
    
    923
    -                 when (member (char str i) '(#\Space #\Tab #\Newline #\Return))
    
    924
    -                 do (error 'hex-parse-error :text str :message "Internal whitespace detected"))
    
    925
    -
    
    926
    -           (let* ((sig-start (+ start 2))
    
    927
    -                  (dot-pos (position #\. str :start sig-start :end p-pos))
    
    928
    -                  (exp-start (1+ p-pos)))
    
    929
    -             (when (or (= sig-start p-pos) 
    
    930
    -                       (and dot-pos (= (1+ sig-start) p-pos) (= sig-start dot-pos)))
    
    931
    -               (error 'hex-parse-error :text str :message "No hex digits in significand"))
    
    932
    -             
    
    933
    -             (handler-case
    
    934
    -                 (let* ((frac-hex-len (if dot-pos (- p-pos (1+ dot-pos)) 0))
    
    935
    -                        (significand-int 
    
    936
    -                         (if (null dot-pos)
    
    937
    -                             (parse-integer str :start sig-start :end p-pos :radix 16)
    
    938
    -                             (let ((leading (if (= sig-start dot-pos) 0 
    
    939
    -                                                (parse-integer str :start sig-start :end dot-pos :radix 16)))
    
    940
    -                                   (trailing (if (= (1+ dot-pos) p-pos) 0
    
    941
    -                                                 (parse-integer str :start (1+ dot-pos) :end p-pos :radix 16))))
    
    942
    -                               (+ (ash leading (* 4 frac-hex-len)) trailing))))
    
    943
    -                        (raw-exponent (parse-integer str :start exp-start :end len))
    
    944
    -                        ;; A leading zero digit in hex-float notation denotes a subnormal-style format.
    
    945
    -                        (starts-with-zero (char= (char str sig-start) #\0)))
    
    946
    -                   
    
    947
    -                   (let ((val (if starts-with-zero
    
    948
    -                                  ;; Subnormal path: Fixed binary floor of -1022.
    
    949
    -                                  ;; Shifted by (4 * frac-hex-len) to align integer bits.
    
    950
    -                                  (scale-float (float significand-int 1.0d0) 
    
    951
    -                                               (- -1022 (* 4 frac-hex-len)))
    
    952
    -                                  ;; Normalized path: Use provided exponent, 
    
    953
    -                                  ;; adjusted for the integer shift.
    
    954
    -                                  (scale-float (float significand-int 1.0d0) 
    
    955
    -                                               (- raw-exponent (* 4 frac-hex-len))))))
    
    956
    -                     (* sign val)))
    
    957
    -               (error (c) (error 'hex-parse-error :text str :message (format nil "~A" c)))))))))))
    
    958
    -
    
    959
    -
    
    960
    -
    
    961
    -
    
    962
    -
    
    963
    -#+nil
    
    964
    -(defun parse-hex-float (str)
    
    965
    -  "Parses C-style hex strings into double-floats.  \"inf\" returns
    
    966
    -  DOUBLE-FLOAT-POSITIVE-INFINITY and \"-inf\" returns
    
    967
    -  DOUBLE-FLOAT-NEGATIVE-INFINITY.  \"nan\" returns :NAN."
    
    968
    -  (let* ((str (string-trim '(#\Space #\Tab #\Newline #\Return) (string-downcase str)))
    
    969
    -         (len (length str)))
    
    970
    -    (when (zerop len)
    
    971
    -      (error 'hex-parse-error :text str :message "Empty string"))
    
    972
    -    (let* ((has-sign (or (char= (char str 0) #\-)
    
    973
    -                         (char= (char str 0) #\+)))
    
    974
    -           (sign (if (and has-sign (char= (char str 0) #\-))
    
    975
    -                     -1 1))
    
    976
    -           (start (if has-sign 1 0)))
    
    977
    -      (cond
    
    978
    -        ((string= str "inf" :start1 start)
    
    979
    -         (if (= sign 1)
    
    980
    -	     double-float-positive-infinity
    
    981
    -	     double-float-negative-infinity))
    
    982
    -        ((string= str "nan" :start1 start)
    
    983
    -         :nan)
    
    984
    -        (t
    
    985
    -         (unless (and (<= (+ start 2) len)
    
    986
    -                      (string= str "0x" :start1 start :end1 (+ start 2)))
    
    987
    -           (error 'hex-parse-error :text str :message "Missing '0x' prefix"))
    
    988
    -         (let ((p-pos (position #\p str :start start)))
    
    989
    -           (unless p-pos
    
    990
    -             (error 'hex-parse-error :text str :message "Missing exponent 'p'"))
    
    991
    -           (unless (loop for i from start below len
    
    992
    -			 never (member (char str i)
    
    993
    -				       '(#\Space #\Tab #\Newline #\Return)))
    
    994
    -             (error 'hex-parse-error :text str :message "Internal whitespace detected"))
    
    995
    -           (let* ((sig-start (+ start 2))
    
    996
    -                  (dot-pos (position #\. str :start sig-start :end p-pos))
    
    997
    -                  (exp-start (1+ p-pos)))
    
    998
    -             (when (or (= sig-start p-pos) 
    
    999
    -                       (and dot-pos
    
    1000
    -                            (= (1+ sig-start) p-pos)
    
    1001
    -                            (= sig-start dot-pos)))
    
    1002
    -               (error 'hex-parse-error :text str :message "No hex digits in significand"))
    
    1003
    -             (handler-case
    
    1004
    -                 (let* ((frac-hex-len (if dot-pos
    
    1005
    -                                          (- p-pos (1+ dot-pos))
    
    1006
    -                                          0))
    
    1007
    -                        (significand-int 
    
    1008
    -                          (if (null dot-pos)
    
    1009
    -                              (parse-integer str :start sig-start :end p-pos :radix 16)
    
    1010
    -                              (let ((leading (if (= sig-start dot-pos)
    
    1011
    -                                                 0 
    
    1012
    -                                                 (parse-integer str :start sig-start :end dot-pos :radix 16)))
    
    1013
    -                                    (trailing (if (= (1+ dot-pos) p-pos)
    
    1014
    -                                                  0
    
    1015
    -                                                  (parse-integer str :start (1+ dot-pos) :end p-pos :radix 16))))
    
    1016
    -				(+ (ash leading (* 4 frac-hex-len))
    
    1017
    -                                   trailing))))
    
    1018
    -                        (raw-exponent (parse-integer str :start exp-start :end len))
    
    1019
    -                        ;; Scale: each fractional nibble reduces binary exponent by 4
    
    1020
    -                        (val (scale-float (float significand-int 1.0d0) 
    
    1021
    -                                          (- raw-exponent
    
    1022
    -                                             (* 4 frac-hex-len)))))
    
    1023
    -                   (* sign val))
    
    1024
    -               (error (c)
    
    1025
    -                 (error 'hex-parse-error :text str :message (format nil "~A" c)))))))))))
    
    1026 728
     
    
    1027 729
     
    
    1028 730
     

  • src/tools/worldcom.lisp
    ... ... @@ -221,6 +221,7 @@
    221 221
     (comf "target:code/misc")
    
    222 222
     (comf "target:code/misc-doc")
    
    223 223
     (comf "target:code/extensions" :byte-compile t)
    
    224
    +(comf "target:code/ext-code")
    
    224 225
     (comf "target:code/commandline")
    
    225 226
     (comf "target:code/env-access")
    
    226 227
     
    

  • src/tools/worldload.lisp
    ... ... @@ -44,6 +44,7 @@
    44 44
     
    
    45 45
     
    
    46 46
     (maybe-byte-load "target:code/extensions")
    
    47
    +(maybe-byte-load "target:code/ext-code")
    
    47 48
     (maybe-byte-load "target:code/defmacro")
    
    48 49
     (maybe-byte-load "target:code/sysmacs")
    
    49 50