RISC-V에서 FP는 F(single)나 D(double) extension을 설치해서 사용할 수 있다.
precision에 따라 하위 32비트만 쓰는지 전부(64비트)쓰는지 결정한다.
명령어가 따로 있는 이유
FP는 fn 포맷인 레지스터를 따로 사용하므로 이런 FP 레지스터에서만 작동하는 FP 명령어가 있다.
flw, fsw : single precision
fld, fsd : double precision
fadd.s, fsub.s, fmul.s, fdiv.s, fsqrt.s : single precision
fadd.d, fsub.d, fmul.d, fdiv.d, fsqrt.d : double precision
즉, int명령어에서 앞에 f를 붙이고 뒤에 precision 종류를 붙여 명령어를 만든다. 오퍼랜드 구성 또한 정수 계산과 같다.
변수 선언하기
정수 명령어에서는 변수를 빈 레지스터에 더하는 식으로 선언했었다.
addi x5, x5, 32부동 소수점 명령어에서는 미리 어떤 레지스터에 값을 적재한 뒤 불러온다.
flw f0, const5(x3)여기서 const5는 오프셋을 뜻하며 다른 숫자로 바뀔 수도 있다.
비교 연산자도 비슷한 방식으로 구현되어 있다.
feq.s, flt.s, fle.s : single precision
feq.d, flt.d, fle.d : double precision
bge와 다른 점은?
branch 명령어와 다른 점은 분기 관리 능력이 없다는 점이다.
FP 비교연산자의 rd는 계산 결과를 의미한다. (0-false, 1-true) 따라서 FP에서 분기 관리를 하려면 비교연산자 + 분기 이동 명령어를 사용해야 한다.
fmul.s f0, f0, f1
반올림을 하기 위해서는 유효한 숫자 범위보다 더 많은 숫자를 알아야 한다. 이 숫자들을 significant digit 이라고 부른다.
fraction이 10비트인데 표현하려는 숫자가 범위를 초과하는 상황을 가정해보자.
예시)
1001011100100001
우선 표현 가능 범위에서 숫자를 자른다.
1001011100 / 100001
표현하려는 범위를 넘어서는 첫 번째, 두 번째 비트를 각각 guard, round라고 한다.
sticky 는 round 후로 숫자가 끝날 때까지 1이 하나라도 있으면 1, 전부 0이면 0으로 정해진다.
이 경우에 guard=1, round=0. sticky=1 이 된다.
세 숫자의 경우의 수에 따라 어떻게 자리 맞춤을 할지 정하면 된다.
round to nearest even 예시
1.001___ 이라는 숫자가 있다고 하자. 빈 공간에 들어올 수 있는 수 중에서 가운데 값은 100이다.
- 1.001011 -> 100 > 011 이므로 버림
- 1.001101 -> 100 < 101 이므로 올림
- 1.001100 -> 100 = 100 이 때 바로 위 비트가 1(홀수)이므로 올림
- 1.000100 -> 100 = 100 이 때 바로 위 비트가 0(짝수)이므로 내림
이처럼 가까운 값을 최대한 짝수로 만들어주는 방법이다.
문제 : 2.56 10^0 + 2.34 10^2, significant digits : 3
이처럼 FP는 값이 정밀하지 못한 부분이 있기 때문에 어떤 연산을 하느냐에 따라 병렬화를 할지 말지 잘 생각해야 한다.
고정 소수점 실수는 부호 비트 S, 정수 부분, 소수 부분으로 나누어 표현한다.
정수 부분을 IWL, 소수 부분을 FWL이라고 하며 부호까지 합친 전체 길이를 WL(word length)이라고 부른다.
예시) 4.25를 8비트로 나타내보자. 고정 소수점은 부동 소수점처럼 비트 수의 범위를 제한해 둘 필요가 없다. 따라서 정수부, 소수부 비트 수의 표준이 없고 그때그때 다르다. 이번에는 정수부 5비트, 소수부 2비트라고 가정하자.
2진수로 변환하면 100.01이고 양수이므로 0010001, 앞에 sign bit를 붙여 00010001이 된다.
8.5 -> 1000.1
0 001000 1
0 0100010